线段树学习、

来源:互联网 发布:淘宝上的旺旺号是什么 编辑:程序博客网 时间:2024/06/05 02:34

这篇博客比较便于理解:Gakki

联系衔接:你想变强吗

单点更新区间加减懒惰标记这些都是基础、

POJ 2886

题意:给出n个人的名字和他手中的数字ai,n个人围成一圈,给出一个数字k,代表k这个人第一个出去,如果k这个人的数字是正数,代表顺时针走ak次的人出去,负数就逆时针,现在要求一个F(p)的最大值,F(p)代表p的因子个数,比如6 有因子个数 1 2 3 6所以F(6) = 4,令第i个人出去的值是F(i),现在要求F(i)的最大值,最大值相同输出最先出去的那个人

思路:首先我们可以用素数筛求出每个数的因子个数,那么我们就可以知道1到n中那个数的因子最多了,使用线段树维护这个区间还有多少人,假设第i轮是第p个出去,那么在区间里找到第p个位置即可,mod实际上代表的是还剩下多少个人,由于数很大,要进行模运算,但原本的位置是1 ~ mod, mod对应的是0 ~ mod - 1,所以这里我们要转化一下,把1~n转化到0~mod - 1,然后再转化回1 ~ n

#include <cstdio>#include <cstring>#include <cmath>#include <cstdlib>#include <ctime>#include <iostream>#include <algorithm>#include <sstream>#include <string>#include <vector>#include <queue>#include <stack>#include <map>#include <set>#include <utility>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 5e5 + 10;const int INF = 1e9 + 10;bool isprime[qq];int prime[qq];int n, m, top;char name[qq][15];int num[qq];int tree[qq << 2];int factor[qq], of[qq];void Init() {top = 0;for(int i = 0; i < qq; ++i) {of[i] = i;factor[i] = 1;}for(int i = 2; i < qq; ++i) {if(!isprime[i]) {prime[++top] = i;}for(int j = 1; j <= top && i * prime[j] < qq; ++j) {isprime[i * prime[j]] = true;if(i % prime[j] == 0) {break;}}}for(int i = 1; i <= top; ++i) {for(int j = prime[i]; j < qq; j += prime[i]) {int cnt = 1;while(of[j] % prime[i] == 0) {of[j] /= prime[i];cnt++;}factor[j] *= cnt;}}}void PushUp(int rt) {tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];}void BuildTree(int l, int r, int rt) {tree[rt] = (r - l + 1);if(l == r)return;int m = (l + r) >> 1;BuildTree(l, m, rt << 1);BuildTree(m + 1, r, rt << 1 | 1);}int Change(int l, int r, int rt, int k) {tree[rt]--;if(l == r)return l;int m = (l + r) >> 1;if(tree[rt << 1] >= k) {return Change(l, m, rt << 1, k);}return Change(m + 1, r, rt << 1 | 1, k - tree[rt << 1]);}map<int, bool> mp;int main(){Init();int &mod = tree[1];while(scanf("%d%d", &n, &m) != EOF) {for(int i = 1; i <= n; ++i) {scanf("%s %d", name[i], num + i);}BuildTree(1, n, 1);int maxn = 0;for(int i = 1; i <= n; ++i) {maxn = max(maxn, factor[i]);}int cnt;for(int i = 1; i <= n; ++i) {if(maxn == factor[i]) {cnt = i;break;}}int pos = 0;num[pos] = 0;for(int i = 0; i < cnt; ++i) {if(num[pos] > 0) {m = ((m + num[pos] - 2) % mod + mod) % mod + 1;} else {m = ((m + num[pos] - 1) % mod + mod) % mod + 1;}pos = Change(1, n, 1, m);}printf("%s %d\n", name[pos], maxn);}return 0;}



POJ 2528

题意:有一块广告版长度无限不考虑宽度,顺序给出n个广告牌,它的会贴在广告牌的[ai, bi],顺序去贴广告牌,问最后能看到几个广告

思路:一个很简单的思路就是直接给区间[ai, bi]赋值标记数字,最后统计有多少不同的数字,但是范围太大了 达到了1e9,这样建树是不可的,但是注意n的范围很小,所以我们可以考虑把坐标给离散化,把出现的区间数字从小到大排序然后映射到区间较小的范围去,最后实现最开始的简单思路统计即可

#include <cstdio>#include <cstring>#include <cmath>#include <cstdlib>#include <ctime>#include <iostream>#include <algorithm>#include <sstream>#include <string>#include <vector>#include <queue>#include <stack>#include <map>#include <set>#include <utility>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 1e4 + 10;const int INF = 1e9 + 10;int li[qq], ri[qq], num;int x[qq << 3], col[qq << 4], ans;bool hs[qq];void Init() {mst(col, -1);mst(hs, false);}void PushDown(int rt) {if(col[rt] != -1) {col[rt << 1] = col[rt << 1 | 1] = col[rt];col[rt] = -1;}}int Search(int l, int r, int val) {while(l <= r) {int m = (l + r) >> 1;if(x[m] == val)return m;else if(x[m] < val) l = m + 1;elser = m - 1;}return 0;}void UpDate(int l, int r, int rt, int x, int y, int val) {if(x <= l && r <= y) {col[rt] = val;return;}PushDown(rt);int m = (l + r) >> 1;if(m >= x)UpDate(l, m, rt << 1, x , y, val);if(m < y)UpDate(m + 1, r, rt << 1 | 1, x, y, val);}void Query(int l, int r, int rt) {if(l == r) {if(col[rt] == -1)return;if(!hs[col[rt]])++ans;hs[col[rt]] = true;return;}PushDown(rt);int m = (l + r) >> 1;Query(l, m, rt << 1);Query(m + 1, r, rt << 1 | 1);}int main(){int t;scanf("%d", &t);while(t--) {Init();int n;scanf("%d", &n);int m = 0;for(int i = 0; i < n; ++i) {scanf("%d%d", li + i, ri + i);x[++m] = li[i];x[++m] = ri[i];}sort(x + 1, x + 1 + m);int num = 1;for(int i = 2; i <= m; ++i) {if(x[i] != x[i - 1])x[++num] = x[i];}for(int i = num; i > 1; --i) {if(x[i] - x[i - 1] > 1)x[++num] = x[i - 1] + 1;}sort(x + 1, x + 1 + num);for(int i = 0; i < n; ++i) {int l = Search(1, num, li[i]);int r = Search(1, num, ri[i]);UpDate(1, num, 1, l, r, i + 1);}ans = 0;Query(1, num, 1);printf("%d\n", ans);}return 0;}



POJ2777

区间覆盖操作

题意:给出L,颜色总数T以及O次操作,每次操作

C 1 1 2P 1 2
第一种代表把区间1 1 内的点染成颜色2

第二种询问区间1 2内有多少种颜色

最初0 ~ L内的点都是颜色1

思路:线段树的区间覆盖问题,这题要好好体会一下,之后很多题都运用到了这种思想

#include <cstdio>#include <cstring>#include <cmath>#include <cstdlib>#include <ctime>#include <iostream>#include <algorithm>#include <sstream>#include <string>#include <vector>#include <queue>#include <stack>#include <map>#include <set>#include <utility>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 100000 + 10;const int INF = 1e9 + 10;int color[qq << 2], lazy[qq << 2];int n, T, Q;void PushDown(int rt) {if(color[rt] != -1) {color[rt << 1] = color[rt << 1 | 1] = color[rt];color[rt] = -1;}/*if(lazy[rt] != -1) {lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];lazy[rt] = -1; }*/}void Change(int l, int r, int rt, int x, int y, int val) {if(x <= l && r <= y) {color[rt] = val;//lazy[rt] = val;return;}PushDown(rt);int m = (l + r) >> 1;if(m >= x)Change(l, m, rt << 1, x, y, val);if(m < y)Change(m + 1, r, rt << 1 | 1, x, y, val);}int ans;bool hs[35];void Query(int l, int r, int rt, int x, int y) {if(color[rt] != -1) {if(!hs[color[rt]])++ans;hs[color[rt]] = true;return;}PushDown(rt);int m = (l + r) >> 1;if(m >= x)Query(l, m, rt << 1, x, y);if(m < y)Query(m + 1, r, rt << 1 | 1, x, y);}int main(){scanf("%d%d%d", &n, &T, &Q);char st[5];int a, b, c;color[1] = 1;while(Q--) {scanf("%s%d%d", st, &a, &b);if(a > b) {swap(a, b);}if(st[0] == 'P') {mst(hs, false);ans = 0;Query(1, n, 1, a, b);printf("%d\n", ans);} else {scanf("%d", &c);Change(1, n, 1, a, b, c);}}return 0;}



POJ3225

这题是真的难想、看了聚聚们的分析才懂

参考:Gakki

说一点自己做这题的感想吧、首先这里要求的是区间,但我们线段树维护的只是点,所以这里用到了这种用偶数表示点奇数来表示区间的这样一种想法(太妙了), 之后就是区间之间的覆盖,覆盖很简单,但是难点在于区间01互换,这里用lazy标记来标志区间是否互换,覆盖的操作又可以去除区间01互换操作带来的影响。

#include <cstdio>#include <cstring>#include <cmath>#include <cstdlib>#include <ctime>#include <iostream>#include <algorithm>#include <sstream>#include <string>#include <vector>#include <queue>#include <stack>#include <map>#include <set>#include <utility>using namespace std;#define LL long long#define pb push_back#define mk make_pair#define mst(a, b)memset(a, b, sizeof a)#define REP(i, x, n)for(int i = x; i <= n; ++i)const int MOD = 1e9 + 7;const int qq = 65535 * 2 + 2;const int INF = 1e9 + 10;int cover[qq << 2], lazy[qq << 2];bool mark[qq];void PushDown(int rt) {if(cover[rt] != -1)cover[rt] ^= 1;elselazy[rt] ^= 1;}void PushDate(int rt) {if(cover[rt] != -1) {cover[rt << 1] = cover[rt << 1 | 1] = cover[rt];lazy[rt << 1] = lazy[rt << 1 | 1] = 0;cover[rt] = -1;}if(lazy[rt]) {PushDown(rt << 1);PushDown(rt << 1 | 1);lazy[rt] = 0;}}void UpDate(int l, int r, int rt, int x, int y, int ch) {if(x <= l && r <= y) {if(ch == 'U')cover[rt] = 1, lazy[rt] = 0;else if(ch == 'D')cover[rt] = lazy[rt] = 0;else if(ch == 'S' || ch == 'C')PushDown(rt);return;}PushDate(rt);int m = (l + r) >> 1;if(m >= x)UpDate(l, m, rt << 1, x, y, ch);else if(ch == 'I' || ch == 'C')cover[rt << 1] = lazy[rt << 1] = 0;if(m < y)UpDate(m + 1, r, rt << 1 | 1, x, y, ch);else if(ch == 'I' || ch == 'C')cover[rt << 1 | 1] = lazy[rt << 1 | 1] = 0;}void Query(int l, int r, int rt) {if(cover[rt] == 1) {for(int i = l; i <= r; ++i) {mark[i] = true;}return;}if(cover[rt] == 0)return;if(l == r)return;PushDate(rt);int m = (l + r) >> 1;Query(l, m, rt << 1);Query(m + 1, r, rt << 1 | 1);}int main(){int l, r;char a, b, c;while(scanf("%c %c%d,%d%c", &a, &b, &l, &r, &c) != EOF) {l <<= 1, r <<= 1;if(b == '(')l++;if(c == ')')r--;if(l > r) {if(a == 'I' || a == 'C')cover[1] = lazy[1] = 0;} else {UpDate(0, qq, 1, l, r, a);}getchar();}bool f = false;int s = -1, t = -1;Query(0, qq, 1);for(int i = 0; i < qq; ++i) {if(mark[i])s = (s == -1 ? i : s) , t = i;else if(s != -1) {if(f)printf(" ");f = true;printf("%c%d,%d%c", s & 1 ? '(' : '[', s >> 1, (t + 1) >> 1, t & 1 ? ')' : ']');s = -1;}}if(!f)printf("empty set");puts("");return 0;}


UESTC 360

参考:聚聚