线段树专题
①POJ3667
题目链接:http://poj.org/problem?id=3667
思路:pushup为合并,向上更新。pushdown为向下更新,lazy存放待更新的操作,1为置满,0为无操作,-1为置空。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N = 5e4 + 5; struct node { int l,r,lazy,ls,rs,ms; }tree[N<<2]; void pushup(int i) { int mid = (tree[i].l + tree[i].r) >> 1; if(tree[i*2].ls == mid - tree[i].l + 1) tree[i].ls = tree[i*2].ls + tree[i*2 + 1].ls; else tree[i].ls = tree[i*2].ls; if(tree[i*2 + 1].rs == tree[i].r - mid) tree[i].rs = tree[i*2 + 1].rs + tree[i*2].rs; else tree[i].rs = tree[i*2 + 1].rs; tree[i].ms = max(max(tree[i*2].ms,tree[i*2 + 1].ms),tree[i*2].rs + tree[i*2 + 1].ls); } void pushdown(int i) { if(tree[i].lazy) { tree[i*2].lazy = tree[i*2 + 1].lazy = tree[i].lazy; if(tree[i].lazy == 1) { tree[i*2].ls = tree[i*2].rs = tree[i*2].ms = 0; tree[i*2 + 1].ls = tree[i*2 + 1].rs = tree[i*2 + 1].ms = 0; } else { tree[i*2].ls = tree[i*2].rs = tree[i*2].ms = tree[i*2].r - tree[i*2].l + 1; tree[i*2 + 1].ls = tree[i*2 + 1].rs = tree[i*2 + 1].ms = tree[i*2 + 1].r - tree[i*2 + 1].l + 1; } tree[i].lazy = 0; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].lazy = 0; tree[i].ls = tree[i].rs = tree[i].ms = r - l + 1; if(l == r) return; int mid = (l + r) >> 1; built(i*2,l,mid); built(i*2 + 1,mid + 1,r); } void update(int i,int l,int r,int c) { if(tree[i].l >= l && tree[i].r <= r) { tree[i].lazy = c; if(c == 1) tree[i].ls = tree[i].rs = tree[i].ms = 0; else tree[i].ls = tree[i].rs = tree[i].ms = tree[i].r - tree[i].l + 1; return; } if(tree[i].r < l || tree[i].l > r) return; pushdown(i); update(i*2,l,r,c); update(i*2 + 1,l,r,c); pushup(i); } int query(int i,int len) { pushdown(i); if(tree[i].ls >= len) return tree[i].l; if(tree[i*2].ms >= len) return query(i*2,len); if(tree[i*2].rs + tree[i*2 + 1].ls >= len) { int mid = (tree[i].l + tree[i].r) >> 1; return mid - tree[i*2].rs + 1; } return query(i*2 + 1,len); } int main() { int n,m; while(~scanf("%d %d",&n,&m)) { built(1,1,n); while(m--) { int c; scanf("%d",&c); if(c == 1) { int x; scanf("%d",&x); if(tree[1].ms < x) { puts("0"); continue; } int ans = query(1,x); printf("%d\n",ans); update(1,ans,ans + x - 1,1); } else { int x,y; scanf("%d %d",&x,&y); update(1,x,x + y - 1,-1); } } } return 0; }
②POJ3225
题目链接:http://poj.org/problem?id=3225
#include<cstdio> #define le(i) i<<1 #define ri(i) i<<1|1 using namespace std; const int N = 65535 << 1; struct node { int l,r,lazy,cov;//lazy表示操作:2为互换,1为置满,0为置空,-1为无操作。cov表示状态:1为全包含,0为全不包含,-1为其它 }tree[(N<<2)+5]; int s[(N<<1)+5]; void pushup(int i) { if(tree[le(i)].cov == tree[ri(i)].cov) tree[i].cov = tree[le(i)].cov; else tree[i].cov = -1; } void pushdown(int i) { if(tree[i].lazy != -1) { if(tree[le(i)].lazy != -1 && tree[i].lazy == 2) { if(tree[le(i)].lazy == 2) tree[le(i)].lazy = -1; else tree[le(i)].lazy ^= 1; } else tree[le(i)].lazy = tree[i].lazy; if(tree[ri(i)].lazy != -1 && tree[i].lazy == 2) { if(tree[ri(i)].lazy == 2) tree[ri(i)].lazy = -1; else tree[ri(i)].lazy ^= 1; } else tree[ri(i)].lazy = tree[i].lazy; if(tree[i].lazy == 2) { if(tree[le(i)].cov != -1) tree[le(i)].cov ^= 1; if(tree[ri(i)].cov != -1) tree[ri(i)].cov ^= 1; } else tree[le(i)].cov = tree[ri(i)].cov = tree[i].lazy; tree[i].lazy = -1; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].lazy = -1; tree[i].cov = 0; if(l == r) return; int mid = (l + r) >> 1; built(le(i),l,mid); built(ri(i),mid + 1,r); } void update(int i,int l,int r,int op)//op:0为置空,1为置满,2为互换 { if(r < l) return; if(tree[i].l >= l && tree[i].r <= r) { if(tree[i].lazy != -1 && op == 2) { if(tree[i].lazy == 2) tree[i].lazy = -1; else tree[i].lazy ^= 1; } else tree[i].lazy = op; if(op == 2) { if(tree[i].cov != -1) tree[i].cov ^= 1; } else tree[i].cov = op; return; } if(tree[i].l > r|| tree[i].r < l) return; pushdown(i); update(le(i),l,r,op); update(ri(i),l,r,op); pushup(i); } void query(int i) { if(!tree[i].cov) return; if(tree[i].cov == 1) { for(int k = tree[i].l; k <= tree[i].r; k++) s[k] = 1; return; } else { pushdown(i); query(le(i)); query(ri(i)); } } int main() { char c,l0,r0; int l,r; built(1,0,N); while(scanf(" %c %c%d,%d %c",&c,&l0,&l,&r,&r0) != EOF) { if(l0 == '(') l = l<<1|1; else l <<= 1; if(r0 == ')') r = (r<<1) - 1; else r <<= 1; if(c == 'U') update(1,l,r,1); else if(c == 'I') { update(1,0,l-1,0); update(1,r+1,N,0); } else if(c == 'D') update(1,l,r,0); else if(c == 'C') { update(1,0,l-1,0); update(1,r+1,N,0); update(1,l,r,2); } else update(1,l,r,2); } query(1); int a = -1,flag = 0; for(int i = 0; i <= N + 1; i++) { if(s[i] && a == -1) a = i,flag = 1; if(!s[i] && a != -1) { if(a&1) printf("(%d,",a>>1); else printf("[%d,",a>>1); if(i&1) printf("%d] ",i>>1); else printf("%d) ",i>>1); a = -1; } } if(!flag) printf("empty set"); printf("\n"); return 0; }
③POJ5023
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5023
由于颜色只有30种,所以可以用位存储,网上看到的好神奇,我却只能想到set
#include<bits/stdc++.h> #define le i<<1 #define ri i<<1|1 using namespace std; const int N = 1e6 + 5; int ans; struct node { int l,r,lazy,color; }tree[N<<2]; void pushup(int i) { tree[i].color = tree[le].color | tree[ri].color; } void pushdown(int i) { if(tree[i].lazy) { tree[le].lazy = tree[ri].lazy = tree[i].lazy; tree[le].color = tree[ri].color = 1<<tree[i].lazy; tree[i].lazy = 0; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].lazy = 0; tree[i].color = 4; if(l == r) return; int mid = (l + r) >> 1; built(le,l,mid); built(ri,mid + 1,r); } void update(int i,int l,int r,int c) { if(tree[i].l >= l && tree[i].r <= r) { tree[i].lazy = c; tree[i].color = 1<<c; return; } if(tree[i].l > r || tree[i].r < l) return; pushdown(i); update(le,l,r,c); update(ri,l,r,c); pushup(i); } void query(int i,int l,int r) { if(tree[i].l >= l && tree[i].r <= r) { ans |= tree[i].color; return; } if(tree[i].l > r || tree[i].r < l) return; pushdown(i); query(le,l,r); query(ri,l,r); } int main() { int n,m,c,l,r; char com; while(scanf("%d %d",&n,&m)) { if(!n && !m) break; built(1,1,n); while(m--) { scanf(" %c",&com); if(com == 'P') { scanf("%d %d %d",&l,&r,&c); update(1,l,r,c); } else { scanf("%d %d",&l,&r); query(1,l,r); int flag = 0; for(int i = 1; i <= 30; i++) { ans >>= 1; if(ans & 1) { if(!flag) printf("%d",i), flag = 1; else printf(" %d",i); } } puts(""); } ans = 0; } } return 0; }
④HDU4578
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578
思路:对于操作1、2,设k为增加倍数,b为增加数量,初始状态k = 1,b = 0
有递推公式sum1 = ∑(kx + b) = k*sum1 + b*len
sum2 = ∑(kx + b)² = ∑(k²x² + 2kxb + b²) = k² * sum2 + 2*k*sum1*b + b² * len
sum3 = ∑(kx + b)³ = ∑(k³x³ + 3k²x²b + 3kxb² + b³) = k³ * sum3 + 3 * k² * sum2 * b + 3 * k * sum1 * b² + b³ * len
对于操作3,会覆盖之前的操作,之前的操作应清零
该死的宏定义
#include<bits/stdc++.h> #define N 100005 #define M 10007 #define le i<<1 #define ri i<<1|1 using namespace std; struct node { int l,r,lazy1,lazy2,lazy3,sum1,sum2,sum3; int len() { return (r - l + 1) % M; } }tree[N<<2]; void add_mul(int i,int k,int b) { int s1 = tree[i].sum1 % M; int s2 = tree[i].sum2 % M; int s3 = tree[i].sum3 % M; int len = tree[i].len() % M; tree[i].lazy1 = (k % M * tree[i].lazy1 % M + b % M) % M; tree[i].lazy2 = k % M * tree[i].lazy2 % M; tree[i].sum1 = (k % M * s1 % M + b % M * len % M) % M; tree[i].sum2 = (k % M * k % M * s2 % M + 2 * k % M * s1 % M * b % M + b % M * b % M * len % M) % M; tree[i].sum3 = (k % M * k % M * k % M * s3 % M + 3 * k % M * k % M * s2 % M * b % M + 3 * k % M * s1 % M * b % M * b % M + b % M * b % M * b % M * len % M) % M; } void equ(int i,int c) { tree[i].lazy1 = 0; tree[i].lazy2 = 1; tree[i].lazy3 = c; tree[i].sum1 = c * tree[i].len() % M; tree[i].sum2 = c * c % M * tree[i].len() % M; tree[i].sum3 = c * c % M * c % M * tree[i].len() % M; } void pushup(int i) { tree[i].sum1 = (tree[le].sum1 + tree[ri].sum1) % M; tree[i].sum2 = (tree[le].sum2 + tree[ri].sum2) % M; tree[i].sum3 = (tree[le].sum3 + tree[ri].sum3) % M; } void pushdown(int i) { if(tree[i].lazy3) { equ(le,tree[i].lazy3); equ(ri,tree[i].lazy3); tree[i].lazy3 = 0; } if(tree[i].lazy1 != 0 || tree[i].lazy2 != 1) { add_mul(le,tree[i].lazy2,tree[i].lazy1); add_mul(ri,tree[i].lazy2,tree[i].lazy1); tree[i].lazy1 = 0; tree[i].lazy2 = 1; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].lazy1 = 0; tree[i].lazy2 = 1; tree[i].lazy3 = 0; tree[i].sum1 = tree[i].sum2 = tree[i].sum3 = 0; if(l == r) return; int mid = (l + r) >> 1; built(le,l,mid); built(ri,mid + 1,r); } void update(int i,int l,int r,int op,int c) { if(tree[i].l >= l && tree[i].r <= r) { if(op == 1) add_mul(i,1,c); else if(op == 2) add_mul(i,c,0); else equ(i,c); return; } if(tree[i].l > r || tree[i].r < l) return; pushdown(i); update(le,l,r,op,c); update(ri,l,r,op,c); pushup(i); } int query(int i,int l,int r,int c) { if(tree[i].l >= l && tree[i].r <= r) { if(c == 1) return tree[i].sum1 % M; if(c == 2) return tree[i].sum2 % M; if(c == 3) return tree[i].sum3 % M; } if(tree[i].l > r || tree[i].r < l) return 0; pushdown(i); return (query(le,l,r,c) + query(ri,l,r,c)) % M; } int main() { int n,m; while(scanf("%d %d",&n,&m)) { if(n == 0 && m == 0) break; built(1,1,n); while(m--) { int op,x,y,c; scanf("%d %d %d %d",&op,&x,&y,&c); if(op == 4) printf("%d\n",query(1,x,y,c) % M); else update(1,x,y,op,c); } } return 0; }
⑥HDU3397
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3397
#include<bits/stdc++.h> #define lson i<<1 #define rson i<<1|1 using namespace std; const int N = 1e5 + 5; int str[N]; struct node { int l,r; int lazy; int num1; int ls1,rs1,ms1; int ls0,rs0,ms0; int len() { return r - l + 1; } }tree[N<<2]; void change(int i,int op) { if(op == 2) { swap(tree[i].ls0,tree[i].ls1); swap(tree[i].rs0,tree[i].rs1); swap(tree[i].ms0,tree[i].ms1); tree[i].lazy = 1 - tree[i].lazy; tree[i].num1 = tree[i].len() - tree[i].num1; } else if(op == 1) { tree[i].lazy = 1; tree[i].ls1 = tree[i].rs1 = tree[i].ms1 = tree[i].num1 = tree[i].len(); tree[i].ls0 = tree[i].rs0 = tree[i].ms0 = 0; } else { tree[i].lazy = 0; tree[i].ls0 = tree[i].rs0 = tree[i].ms0 = tree[i].len(); tree[i].ls1 = tree[i].rs1 = tree[i].ms1 = tree[i].num1 = 0; } } void pushup(int i) { if(tree[lson].ls1 == tree[lson].len()) tree[i].ls1 = tree[lson].ls1 + tree[rson].ls1; else tree[i].ls1 = tree[lson].ls1; if(tree[rson].rs1 == tree[rson].len()) tree[i].rs1 = tree[rson].rs1 + tree[lson].rs1; else tree[i].rs1 = tree[rson].rs1; if(tree[lson].ls0 == tree[lson].len()) tree[i].ls0 = tree[lson].ls0 + tree[rson].ls0; else tree[i].ls0 = tree[lson].ls0; if(tree[rson].rs0 == tree[rson].len()) tree[i].rs0 = tree[rson].rs0 + tree[lson].rs0; else tree[i].rs0 = tree[rson].rs0; tree[i].ms1 = max(max(tree[lson].ms1,tree[rson].ms1),tree[lson].rs1 + tree[rson].ls1); tree[i].ms0 = max(max(tree[lson].ms0,tree[rson].ms0),tree[lson].rs0 + tree[rson].ls0); tree[i].num1 = tree[lson].num1 + tree[rson].num1; } void pushdown(int i) { if(tree[i].lazy != -1) { change(lson,tree[i].lazy); change(rson,tree[i].lazy); tree[i].lazy = -1; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].lazy = -1; if(l == r) { tree[i].num1 = str[l]; tree[i].ls1 = tree[i].rs1 = tree[i].ms1 = str[l]; tree[i].ls0 = tree[i].rs0 = tree[i].ms0 = str[l]^1; return; } int mid = (l + r) >> 1; built(lson,l,mid); built(rson,mid+1,r); pushup(i); } void update(int i,int l,int r,int op) { if(tree[i].l >= l && tree[i].r <= r) { change(i,op); return; } if(tree[i].l > r || tree[i].r < l) return; pushdown(i); update(lson,l,r,op); update(rson,l,r,op); pushup(i); } int query(int i,int l,int r,int op) { if(tree[i].l >= l && tree[i].r <= r) { if(op == 3) return tree[i].num1; else return tree[i].ms1; } if(tree[i].l > r || tree[i].r < l) return 0; pushdown(i); if(op == 3) return query(lson,l,r,op) + query(rson,l,r,op); else { int res1 = min(tree[lson].rs1,tree[lson].r - l + 1) + min(tree[rson].ls1,r - tree[rson].l + 1); int res2 = max(query(lson,l,r,op),query(rson,l,r,op)); return max(res1,res2); } } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d %d",&n,&m); for(int i = 0; i < n; i++) scanf("%d",str+i); built(1,0,n-1); while(m--) { int op,a,b; scanf("%d %d %d",&op,&a,&b); if(op <= 2) update(1,a,b,op); else printf("%d\n",query(1,a,b,op)); } } return 0; }
⑦HDU3016(DAG最长路DP + 线段树区间更新)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3016
题意:本来看错了,以为板上都可以跳。其实只能在板的两端垂直下落。
//不得不承认专项练习还是有个弊端,就是知道它归类于线段树就会拼命往线段树想,要是直接上手的话我肯定想不到。
思路:downl,downr分别表示从该板左右两端跳会落到哪块板子上(id),这样就可以利用线段树建立出一个DAG。然后就是经典的最长路DP。
#include<bits/stdc++.h> #define lson i<<1 #define rson i<<1|1 using namespace std; const int N = 1e5 + 5; const int inf = 0x3f3f3f3f; int dp[N]; struct Board { int l,r; int h,val; int downl,downr; bool operator < (Board t) { return h < t.h; } }board[N]; struct node { int l,r; int cov; }tree[N<<2]; void pushup(int i) { if(tree[lson].cov == tree[rson].cov) tree[i].cov = tree[lson].cov; else tree[i].cov = -1; } void pushdown(int i) { if(tree[i].cov != -1) { tree[lson].cov = tree[rson].cov = tree[i].cov; tree[i].cov = -1; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].cov = 0; if(l == r) return; int mid = (l + r) >> 1; built(lson,l,mid); built(rson,mid+1,r); } void update(int i,int l,int r,int id) { if(tree[i].l >= l && tree[i].r <= r) { tree[i].cov = id; return; } if(tree[i].l > r || tree[i].r < l) return; pushdown(i); update(lson,l,r,id); update(rson,l,r,id); pushup(i); } int query(int i,int c) { if(tree[i].cov != -1) return tree[i].cov; int mid = (tree[i].l + tree[i].r) >> 1; if(c <= mid) return query(lson,c); else return query(rson,c); } int main() { int n; while(~scanf("%d",&n)) { for(int i = 1; i <= n; i++) scanf("%d %d %d %d",&board[i].h,&board[i].l,&board[i].r,&board[i].val); sort(board + 1,board + n + 1); built(1,1,100000); for(int i = 1; i <= n; i++) { board[i].downl = query(1,board[i].l); board[i].downr = query(1,board[i].r); update(1,board[i].l,board[i].r,i); } memset(dp,-inf,sizeof(dp)); dp[n] = 100 + board[n].val; for(int i = n; i >= 1; i--) { if(dp[i] < 0) dp[i] = -inf; dp[board[i].downl] = max(dp[board[i].downl], dp[i] + board[board[i].downl].val); dp[board[i].downr] = max(dp[board[i].downr], dp[i] + board[board[i].downr].val); } if(dp[0] <= 0) puts("-1"); else printf("%d\n",dp[0]); } return 0; }
⑧POJ3577
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3577
#include<bits/stdc++.h> #define lson i<<1 #define rson i<<1|1 using namespace std; const int N = 1e6 + 5; bool yes[100005]; struct node { int l,r; int lazy,maxm; }tree[N<<2]; void pushdown(int i) { if(tree[i].lazy) { tree[lson].lazy += tree[i].lazy; tree[rson].lazy += tree[i].lazy; tree[lson].maxm += tree[i].lazy; tree[rson].maxm += tree[i].lazy; tree[i].lazy = 0; } } void built(int i,int l,int r) { tree[i].l = l; tree[i].r = r; tree[i].lazy = tree[i].maxm = 0; if(l == r) return; int mid = (l + r) >> 1; built(lson,l,mid); built(rson,mid+1,r); } void update(int i,int l,int r) { if(tree[i].l >= l && tree[i].r <= r) { tree[i].lazy++; tree[i].maxm++; return; } if(tree[i].l > r || tree[i].r < l) return; pushdown(i); update(lson,l,r); update(rson,l,r); tree[i].maxm = max(tree[lson].maxm,tree[rson].maxm); } int query(int i,int l,int r) { if(tree[i].l >= l && tree[i].r <= r) return tree[i].maxm; if(tree[i].l > r || tree[i].r < l) return 0; pushdown(i); return max(query(lson,l,r),query(rson,l,r)); } int main() { int T,cas = 1,k,Q,a,b; scanf("%d",&T); while(T--) { memset(yes,0,sizeof(yes)); scanf("%d %d",&k,&Q); built(1,1,1000000); for(int i = 1; i <= Q; i++) { scanf("%d %d",&a,&b); if(query(1,a,b-1) < k) { yes[i] = 1; update(1,a,b-1); } } printf("Case %d:\n",cas++); for(int i = 1; i <= Q; i++) { if(yes[i]) printf("%d ",i); } printf("\n\n"); } return 0; }
⑨HDU5316(单点更新,区间合并)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5316
题意:给出一个长度为n的序列,有两种操作,op = 0时求出区间[a,b]中奇偶相间(不含空)的子序列的和的最大值,op = 1时把a改成b
思路:oo表示该节点区间[L,R]上以奇开头奇结尾的子序列的和的最大值,oe,eo,ee同理......
自己之前的查询部分写的有点赘,TLE了,网上看了别人的写了下,发现之前的几题也可以简化下代码量的。
#include<bits/stdc++.h> #define lson i<<1 #define rson i<<1|1 using namespace std; typedef long long ll; const int N = 1e5 + 5; const ll inf = 1e18; ll MAX(ll a,ll b,ll c,ll d) { return max(max(a,b),max(c,d)); } struct node { ll oo,oe,eo,ee; node() {} node(ll oo,ll oe,ll eo, ll ee):oo(oo),oe(oe),eo(eo),ee(ee) {} }tree[N<<2]; node pushup(node a,node b) { node res; res.oo = MAX(a.oo, b.oo, a.oo + b.eo, a.oe + b.oo); res.oe = MAX(a.oe, b.oe, a.oo + b.ee, a.oe + b.oe); res.eo = MAX(a.eo, b.eo, a.eo + b.eo, a.ee + b.oo); res.ee = MAX(a.ee, b.ee, a.eo + b.ee, a.ee + b.oe); return res; } void built(int i,int L,int R) { if(L == R) { if(L & 1) scanf("%lld",&tree[i].oo), tree[i].oe = tree[i].eo = tree[i].ee = -inf; else scanf("%lld",&tree[i].ee), tree[i].oo = tree[i].oe = tree[i].eo = -inf; return; } int mid = (L + R) >> 1; built(lson,L,mid); built(rson,mid+1,R); tree[i] = pushup(tree[lson],tree[rson]); } void update(int i,int L,int R,int pos,ll val) { if(L == R) { if(L & 1) tree[i].oo = val; else tree[i].ee = val; return; } int mid = (L + R) >> 1; if(pos <= mid) update(lson,L,mid,pos,val); else update(rson,mid+1,R,pos,val); tree[i] = pushup(tree[lson],tree[rson]); } node query(int i,int L,int R,int l,int r) { if(L >= l && R <= r) return tree[i]; if(L > r || R < l) return node(-inf,-inf,-inf,-inf); int mid = (L + R) >> 1; return pushup(query(lson,L,mid,l,r),query(rson,mid+1,R,l,r)); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d %d",&n,&m); built(1,1,n); while(m--) { int op,a,b; scanf("%d %d %d",&op,&a,&b); if(op) update(1,1,n,a,b); else { node ans = query(1,1,n,a,b); printf("%lld\n",MAX(ans.oo,ans.oe,ans.eo,ans.ee)); } } } return 0; }