@LinKArftc
2015-10-06T02:40:07.000000Z
字数 8877
阅读 1055
数据结构
线段树通常配合离散化,解决 RMQ 问题,这是一种很神奇的数据结构 = =||。。。
题意:
线段树求逆序对个数
/*
ID: LinKArftc
PROG: 2299.cpp
LANG: C++
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 500010
int num[MAXN], arr[MAXN], tree[MAXN << 2];
int query(int val, int l, int r, int rt) {
if (l == r) {
tree[rt] ++;
return 0;
}
int mid = (l + r) >> 1;
int ret = 0;
if (val <= arr[mid]) ret = query(val, l, mid, rt << 1) + tree[rt << 1 | 1];
else ret = query(val, mid + 1, r, rt << 1 | 1);
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
return ret;
}
int main() {
// freopen("input.txt", "r", stdin);
int n;
while (~scanf("%d", &n) && n) {
for (int i = 1; i <= n; i ++) {
scanf("%d", &num[i]);
arr[i] = num[i];
}
sort(arr + 1, arr + n + 1);
int tol = 1;
for (int i = 2; i <= n; i ++) {
if (arr[i] != arr[tol]) arr[++ tol] = arr[i];
}
memset(tree, 0, sizeof(tree));
long long ans = 0;
for (int i = 1; i <= n; i ++) ans += query(num[i], 1, tol, 1);
printf("%lld\n", ans);
}
return 0;
}
题目大意:给你星星的坐标(y递增,若y相等,x递增),每个星星都有一个等级,规定它的等级就是在它左下方的星星的个数。输入所有星星后,依次输出等级为0到n-1的星星的个数。
/*
ID: LinKArftc
PROG: 2352.cpp
LANG: C++
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 15010
#define M 32010
int tree[M << 2], cnt[N];
int query(int rt, int l, int r, int L, int R) {
int mid = (l + r) >> 1;
if (L <= l && R >= r) return tree[rt];
int ret = 0;
if (L <= mid) ret += query(rt << 1, l, mid, L, R);
if (R > mid) ret += query(rt << 1 | 1, mid + 1, r, L, R);
return ret;
}
void update(int rt, int l, int r, int x) {
if (l == r) {
tree[rt] ++;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) update(rt << 1, l, mid, x);
else update(rt << 1 | 1, mid + 1, r, x);
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
int main() {
// freopen("input.txt", "r", stdin);
int n;
int x, y;
while (~scanf("%d", &n)) {
memset(cnt, 0, sizeof(cnt));
memset(tree, 0, sizeof(tree));
for (int i = 1; i <= n; i ++) {
scanf("%d %d", &x, &y);
x ++;
cnt[query(1, 1, M - 5, 1, x)] ++;
update(1, 1, M - 5, x);
}
for (int i = 0; i < n; i ++) {
printf("%d\n", cnt[i]);
}
}
return 0;
}
区间覆盖
很经典的一道题,不知道哪里写挫了导致 RE,最近写线段树写死了,不想碰了,以后再改。
已经改好了,其实很简单,之前怎么就那么傻逼。。。= =(详见后文)
/*
ID: LinKArftc
PROG: 2528.cpp
LANG: C++
*/
////// RE
//
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10010;
struct Node {
int l, r, id;
} post[maxn];
int vis[maxn], ans;
int ll[maxn], rr[maxn];
bool cmp_l(Node a, Node b) { return a.l < b.l; }
bool cmp_r(Node a, Node b) { return a.r < b.r; }
bool cmp_id(Node a, Node b) { return a.id < b.id; }
struct Post {
bool isCover;
int l, r;
} tree[maxn << 2];
void build(int rt, int l, int r) {
tree[rt].isCover = 0;
tree[rt].l = l;
tree[rt].r = r;
int mid = (l + r) >> 1;
if (l == r) return ;
else {
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
}
bool check(int rt, int l, int r) {
if (tree[rt].isCover == true) return false;
if (tree[rt].l == l && tree[rt].r == r) {
tree[rt].isCover = 1;
return true;
}
bool rec;
int mid = (tree[rt].l + tree[rt].r) >> 1;
if (r <= mid) rec = check(rt << 1, l, r);
else if (l > mid) rec = check(rt << 1 | 1, l, r);
else {
bool r1 = check(rt << 1, l, mid);
bool r2 = check(rt << 1 | 1, mid + 1, r);
rec = r1 || r2;
}
if (tree[rt << 1].isCover && tree[rt << 1 | 1].isCover) tree[rt].isCover = true;
return rec;
}
int main() {
freopen("input.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T --) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++) {
scanf("%d %d", &post[i].l, &post[i].r);
post[i].id = i;
ll[i] = post[i].l;
rr[i] = post[i].r;
}
sort(post + 1, post + n + 1, cmp_l);
int cur = 1;
ll[1] = 1;
for (int i = 2; i <= n; i ++) {
if (post[i].l == post[i-1].l) ll[i] = cur;
else ll[i] = ++ cur;
}
for (int i = 1; i <= n; i ++) {
post[i].l = ll[i];
}
sort(post + 1, post + n + 1, cmp_r);
cur = 1;
rr[1] = 1;
for (int i = 2; i <= n; i ++) {
if (post[i].r == post[i-1].r) {
rr[i] = cur;
}
else rr[i] = ++ cur;
}
for (int i = 1; i <= n; i ++) {
post[i].r = rr[i];
}
sort(post + 1, post + n + 1, cmp_id);
build(1, 1, n);
for (int i = n; i >= 1; i --) {
if (check(1, post[i].l, post[i].r)) {
ans ++;
}
}
printf("%d\n", ans);
}
return 0;
}
/*
ID: LinKArftc
PROG: setment_tree.cpp
LANG: C++
*/
/*
*矩形覆盖为连续型线段树,注意和离散型线段树的区别
*/
typedef long long ll;
const int maxn = 200010;
int tree[maxn << 2];
int num[maxn];
int n, len;
map <int, int> mp;
set <int> st;
set <int> ans;
struct Node {
int a, b;
int id;
} post[maxn];
void update(int rt, int l, int r, int L, int R, int id) {
if (tree[rt]) {
tree[rt << 1] = tree[rt];
tree[rt << 1 | 1] = tree[rt];
tree[rt] = 0;
}
if (L <= l && R >= r) {
tree[rt] = id;
return;
}
int mid = (l + r) >> 1;
if (R <= mid) update(rt << 1, l, mid, L, R, id);
else if (L >= mid) update(rt << 1 | 1, mid, r, L, R, id);
else {
update(rt << 1, l, mid, L, R, id);
update(rt << 1 | 1, mid, r, L, R, id);
}
}
void query(int rt, int l, int r) {
if (tree[rt]) {
ans.insert(tree[rt]);
return;
}
if (r - l <= 1) return;
int mid = (l + r) >> 1;
query(rt << 1, l, mid);
query(rt << 1 | 1, mid, r);
}
int main() {
//input;
memset(tree, 0, sizeof(tree));
scanf("%d %d", &n, &len);
for (int i = 1; i <= n; i ++) {
scanf("%d %d", &post[i].a, &post[i].b);
post[i].id = i;
st.insert(post[i].a);
st.insert(post[i].b);
}
int cur = 1;
while (!st.empty()) {
mp[*st.begin()] = cur ++;
st.erase(st.begin());
}
for (int i = 1; i <= n; i ++) {
post[i].a = mp[post[i].a];
post[i].b = mp[post[i].b];
}
for (int i = 1; i <= n; i ++) {
update(1, 1, n << 1, post[i].a, post[i].b, post[i].id);
}
query(1, 1, n << 1);
printf("%d\n", ans.size());
return 0;
}
求区间最大值最小值
裸线段树,配合离散化
/*
ID: LinKArftc
PROG: 3264.cpp
LANG: C++
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 50005
int num[N], arr[N];
struct Node {
int ma, mi;
} tree[N << 2];
void build(int rt, int l, int r) {
if (l == r) {
tree[rt].ma = num[l];
tree[rt].mi = num[l];
return;
}
int mid = (l + r) / 2;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
tree[rt].ma = max(tree[rt << 1].ma, tree[rt << 1 | 1].ma);
tree[rt].mi = min(tree[rt << 1].mi, tree[rt << 1 | 1].mi);
}
int query_ma(int rt, int l, int r, int L, int R) {
if (L <= l && R >= r) return tree[rt].ma;
int mid = (l + r) / 2;
if (R <= mid) return query_ma(rt << 1, l, mid, L, R);
if (L > mid) return query_ma(rt << 1 | 1, mid + 1, r, L, R);
return max(query_ma(rt << 1, l, mid, L, R), query_ma(rt << 1 | 1, mid + 1, r, L, R));
}
int query_mi(int rt, int l, int r, int L, int R) {
if (L <= l && R >= r) return tree[rt].mi;
int mid = (l + r) / 2;
if (R <= mid) return query_mi(rt << 1, l, mid, L, R);
if (L > mid) return query_mi(rt << 1 | 1, mid + 1, r, L, R);
return min(query_mi(rt << 1, l, mid, L, R), query_mi(rt << 1 | 1, mid + 1, r, L, R));
}
int main() {
// freopen("input.txt", "r", stdin);
int n, q;
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i ++) scanf("%d", &num[i]);
build(1, 1, n);
for (int i = 1; i <= q; i ++) {
int a, b;
scanf("%d %d", &a, &b);
int ma = query_ma(1, 1, n, a, b);
int mi = query_mi(1, 1, n, a, b);
printf("%d\n", ma - mi);
}
return 0;
}
区间更新,区间求和
思路:裸线段树,要用 lazy 标记 add,否则肯定会超时
/*
ID: LinKArftc
PROG: 3468.cpp
LANG: C++
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
int n, q;
long long num[N];
struct Node {
long long sum, add;
Node() { sum = 0; add = 0; }
} tree[N << 2];
void build(int rt, int l, int r) {
if (l == r) {
tree[rt].sum = num[l];
return;
}
int mid = (l + r) / 2;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
long long query(int rt, int l, int r, int L, int R) {
if (L <= l && R >= r) return tree[rt].sum + tree[rt].add * (r - l + 1);
if (tree[rt].add != 0) {
tree[rt].sum += tree[rt].add * (r - l + 1);
tree[rt << 1].add += tree[rt].add;
tree[rt << 1 | 1].add += tree[rt].add;
tree[rt].add = 0;
}
int mid = (l + r) >> 1;
if (R <= mid) return query(rt << 1, l, mid, L, R);
if (L > mid) return query(rt << 1 | 1, mid + 1, r, L, R);
return query(rt << 1, l, mid, L, mid) + query(rt << 1 | 1, mid + 1, r, L, R);
}
void update(int rt, int l, int r, int L, int R, long long c) {
if (L <= l && R >= r) {
tree[rt].add += c;
return ;
}
tree[rt].sum += c * (min(R, r) - max(L, l) + 1);
int mid = (l + r) >> 1;
if (R <= mid) {
update(rt << 1, l, mid, L, R, c);
return ;
}
if (L > mid) {
update(rt << 1 | 1, mid + 1, r, L, R, c);
return ;
}
update(rt << 1, l, mid, L, mid, c);
update(rt << 1 | 1, mid + 1, r, L, R, c);
return ;
}
int main() {
// freopen("input.txt", "r", stdin);
while (~scanf("%d %d", &n, &q)) {
for (int i = 1; i <= n; i ++) scanf("%lld", &num[i]);
build(1, 1, n);
char sel;
int a, b;
long long c;
for (int i = 1; i <= q; i ++) {
scanf(" %c", &sel);
if (sel == 'Q') {
scanf("%d %d", &a, &b);
printf("%lld\n", query(1, 1, n, a, b));
} else {
scanf("%d %d %lld", &a, &b, &c);
update(1, 1, n, a, b, c);
}
}
}
return 0;
}
题意: 起重机的机械臂, 由n段组成, 对某一些连接点进行旋转, 询问每次操作后的末端坐标.
思路:这一题相当恶心。。。因为后面的杆子是跟着动的,所以他们整体上的变化的角度都是一样的,这就变成了区间更新的线段树了,查询很简单,就是顶点的坐标。
线段树节点保存该区间(即第 i 到第 j 条杆子的整体,从起点到终点)的一个向量,存了 x 和 y 坐标(这是向量,不是实际在坐标系中的坐标!)。
add 数组就是 lazy 标记。因为一个向量 (x, y) 逆时针转角度 a 以后,向量变成(cos(a) * x - sin(a) * y, sin(a) * x + cos(a) * y),所以,我们在 add 中记录当前区间逆时针转的角度。
用一个 angle 数组存相邻线段之间的角度,通过这个值和到时候要修改成的角度a来换算出逆时针转过的角度
/*
ID: LinKArftc
PROG: 2991.cpp
LANG: C++
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
const double pi = acos(-1.0);
const double eps = 1e-9;
const int maxn = 10010;
struct Node {
double x, y;
int add;
Node(int _x, int _y, int _add) : x(_x), y(_y), add(_add) {}
Node() { add = 0; }
} tree[maxn << 2];
int angle[maxn];
void rotate(int rt, int a) {
double x = tree[rt].x;
double y = tree[rt].y;
double ang = pi / 180 * a;
tree[rt].x = cos(ang) * x - sin(ang) * y;
tree[rt].y = sin(ang) * x + cos(ang) * y;
}
void build(int rt, int l, int r) {
if (l == r) {
scanf("%lf", &tree[rt].y);
tree[rt].x = 0;
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
tree[rt].x = tree[rt << 1].x + tree[rt << 1 | 1].x;
tree[rt].y = tree[rt << 1].y + tree[rt << 1 | 1].y;
}
void update(int rt, int l, int r, int L, int R, int a) {
if(L <= l && R >= r) {
tree[rt].add += a;
rotate(rt, a);
return;
}
if(tree[rt].add) {
rotate(rt << 1, tree[rt].add);
rotate(rt << 1 | 1, tree[rt].add);
tree[rt << 1].add += tree[rt].add;
tree[rt << 1 | 1].add += tree[rt].add;
tree[rt].add = 0;
}
int mid = (r + l) >> 1;
if (L <= mid) update(rt << 1, l, mid, L, R, a);
if (R > mid) update(rt << 1 | 1, mid + 1, r, L, R, a);
tree[rt].x = tree[rt << 1].x + tree[rt << 1 | 1].x;
tree[rt].y = tree[rt << 1].y + tree[rt << 1 | 1].y;
}
int main() {
// freopen("input.txt", "r", stdin);
int n, m;
int cnt = 0;
while (~scanf("%d%d", &n, &m)) {
memset(tree, 0, sizeof(tree));
if(cnt ++) printf("\n");
build(1, 1, n);
for (int i = 1; i <= n; i ++) angle[i] = 180;
while (m --) {
int s, a;
scanf("%d%d",&s, &a);
int x = (a - angle[s] + 360) % 360;
update(1, 1, n, s + 1, n, x);
angle[s] = a;
printf("%.2lf %.2lf\n",tree[1].x + eps, tree[1].y + eps);
}
}
return 0;
}