线段树复习--[kuangbin带你飞] 线段树
来源:互联网 发布:网络鲜花店 上海 编辑:程序博客网 时间:2024/06/10 21:46
温故知新,现在代码水平好了一些,重新刷kuangbin的专题也不那么费劲了。争取理解的透彻一些吧。
区间更新模板:
LL num[maxn],sum[maxn<<2],mark[maxn<<2];void push_up(int root) { sum[root] = sum[root<<1]+sum[root<<1|1];}void push_down(int root,int cl,int cr) { if(mark[root]) { mark[root<<1] += mark[root]; mark[root<<1|1] += mark[root]; sum[root<<1] += mark[root]*cl; sum[root<<1|1] += mark[root]*cr; mark[root] = 0; }}void build(int l,int r,int root) { //建树 if(l == r) { sum[root] = num[l]; return ; } int m = (l+r)>>1; build(l,m,root<<1); build(m+1,r,root<<1|1); //注意m要+1 push_up(root);}void update(int L,int R,LL x,int l,int r,int root) { //成段更新 if(L <= l && r <= R) { mark[root] += x; //延迟更新标记 sum[root] += (r-l+1)*x; return ; } int m = (l+r)>>1; push_down(root,m-l+1,r-m); //下推标记 if(m >= L) update(L,R,x,l,m,root<<1); if(m < R) update(L,R,x,m+1,r,root<<1|1); //注意区间的开闭 push_up(root);}LL query(int L,int R,int l,int r,int root) { //区间查询 if(L <= l && r <= R) { return sum[root]; } int m = (l+r)>>1; if(mark[root]) push_down(root,m-l+1,r-m); LL ans = 0; if(m >= L) ans += query(L,R,l,m,root<<1); if(m < R) ans += query(L,R,m+1,r,root<<1|1); return ans;}
C - A Simple Problem with Integers
题意:
裸的线段树成段更新。
即两种操作,成段更新,区间查询和
题解:
直接用线段树成段更新的方法即可。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn = 1e5+5;LL mod = 1e9+7;double eps = 0.00000001;double PI = acos(-1);LL num[maxn],sum[maxn<<2],mark[maxn<<2];void push_up(int root) { sum[root] = sum[root<<1]+sum[root<<1|1];}void push_down(int root,int cl,int cr) { if(mark[root]) { mark[root<<1] += mark[root]; mark[root<<1|1] += mark[root]; sum[root<<1] += mark[root]*cl; sum[root<<1|1] += mark[root]*cr; mark[root] = 0; }}void build(int l,int r,int root) { if(l == r) { sum[root] = num[l]; return ; } int m = (l+r)>>1; build(l,m,root<<1); build(m+1,r,root<<1|1); push_up(root);}void update(int L,int R,LL x,int l,int r,int root) { if(L <= l && r <= R) { mark[root] += x; sum[root] += (r-l+1)*x; return ; } int m = (l+r)>>1; push_down(root,m-l+1,r-m); if(m >= L) update(L,R,x,l,m,root<<1); if(m < R) update(L,R,x,m+1,r,root<<1|1); push_up(root);}LL query(int L,int R,int l,int r,int root) { if(L <= l && r <= R) { return sum[root]; } int m = (l+r)>>1; if(mark[root]) push_down(root,m-l+1,r-m); LL ans = 0; if(m >= L) ans += query(L,R,l,m,root<<1); if(m < R) ans += query(L,R,m+1,r,root<<1|1); return ans;}int main() { int n,q; scanf("%d%d",&n,&q); for(int i = 1;i <= n;i++) scanf("%I64d",&num[i]); build(1,n,1); clr(mark,0); for(int i = 1;i <= q;i++) { char str[3]; int l,r;LL x; scanf("%s",str); if(str[0] == 'Q') { scanf("%d%d",&l,&r); printf("%I64d\n",query(l,r,1,n,1)); } else { scanf("%d%d%I64d",&l,&r,&x); update(l,r,x,1,n,1); } }}
D - Mayor’s posters (离散化)
题意:
给你一个 1~1e7 的墙,给出一个n,对于每个 i = 1->n , 有两个端点 x1,x2 ,表示x1->x2有的范围有一张新的海报贴在墙上,范围是 x1-x2,问你贴完N个海报后墙上可以看到多少张海报,被完全覆盖的海报不算个数。
题解:
因为范围很大,先对坐标离散化,离散花的时候要注意如果两个坐标距离大于2,那么距离+2,不然是距离+1。
然后用线段树成端更新的作法。
最后我的做法是for循环从1到n都检查一遍,求海报数
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn = 1e5+5;LL mod = 1e9+7;double eps = 0.00000001;pii num[maxn],st[maxn],st1[maxn];int n,col[maxn],f[maxn],dd[maxn],cnt,cc;void init() { clr(col,0); clr(f,0); cnt = 0; cc = 0;}void push_down(int root) { //延迟更新 col[root<<1] = col[root]; col[root<<1|1] = col[root]; f[root<<1] = f[root]; f[root<<1|1] = f[root]; f[root] = 0;}void update(int L,int R,int c,int l,int r,int root) { //成段更新 if(L <= l && r <= R) { col[root] = c; f[root] = c; return ; } int m = (l+r)>>1; if(f[root]) push_down(root); if(m >= L) update(L,R,c,l,m,root<<1); if(m < R) update(L,R,c,m+1,r,root<<1|1);}int query(int pos,int l,int r,int root) { //单点查询 if(l == r && l == pos) { return col[root]; } if(f[root]) push_down(root); int m = (l+r)>>1; if(m >= pos) query(pos,l,m,root<<1); else query(pos,m+1,r,root<<1|1);}bool cmp(pii a,pii b) { if(a.se == b.se) return a.fir < b.fir; return a.se < b.se;}int main() { int t; cin>>t; while(t--) { scanf("%d",&n); init(); for(int i = 1;i <= n;i++) { scanf("%d%d",&num[i].fir,&num[i].se); st[++cnt] = (make_pair(num[i].fir,i)); st[++cnt] = (make_pair(num[i].se,i)); } sort(st+1,st+1+cnt); st[0].fir = st[1].fir-1; for(int i = 1;i <= cnt;i++) { //离散化 if(st[i].fir - st[i-1].fir == 1) st1[i].fir = ++cc; else if(st[i].fir - st[i-1].fir > 1) { cc += 2; st1[i].fir = cc; } else st1[i].fir = cc; st1[i].se = st[i].se; } sort(st1+1,st1+cnt+1,cmp); for(int i = 1;i <= cnt;i += 2) { //printf("%d %d - %d %d\n",st1[i].fir,st1[i].se,st1[i+1].fir,st1[i+1].se); update(st1[i].fir,st1[i+1].fir,i,1,4*n,1); //for(int i = 1;i <= 4*n;i++) printf("%d-",query(i,1,4*n,1)); //cout<<endl; } cnt = 0; for(int i = 1;i <= 4*n;i++) { dd[++cnt] = query(i,1,4*n,1); } sort(dd+1,dd+1+cnt); cc = 0; dd[0] = -1; for(int i = 1;i <= 4*n;i++) if(dd[i] != dd[i-1]) cc++; printf("%d\n",cc-1); } }/*562 35 611 6622 3322 3322 33*/
F - Count the Colors
题意:
和前面一道题目一样,也是给你一个段,有n个操作,给你l,r,c,在区间l,r之间涂颜色c,问你n次之后有多少个可见颜色段。
题解:
和那道题作法一样,都是区间更新,最后单点询问。这里不区间查询是因为颜色的种类不能区间合并,当然也有区间查询的作法,我用的是单点询问。主要是这道题的线段定义方式不一样。
不过这道题目的区间范围不大,所以我们直接把坐标*2,就可以解决问题。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn = 1e6+5;LL mod = 1e9+7;double eps = 0.00000001;int n,col[maxn],f[maxn],dd[maxn],cnt,cc;void init() { clr(col,0); clr(f,0);}void push_down(int root) { //延迟更新 col[root<<1] = col[root]; col[root<<1|1] = col[root]; f[root<<1] = f[root]; f[root<<1|1] = f[root]; f[root] = 0;}void update(int L,int R,int c,int l,int r,int root) { //成段更新 if(L <= l && r <= R) { col[root] = c; f[root] = c; return ; } int m = (l+r)>>1; if(f[root]) push_down(root); if(m >= L) update(L,R,c,l,m,root<<1); if(m < R) update(L,R,c,m+1,r,root<<1|1);}int query(int pos,int l,int r,int root) { //单点查询 if(l == r && l == pos) { return col[root]; } if(f[root]) push_down(root); int m = (l+r)>>1; if(m >= pos) query(pos,l,m,root<<1); else query(pos,m+1,r,root<<1|1);}int main() { while(scanf("%d",&n) != EOF) { init(); int mm = 16005; for(int i = 1;i <= n;i++) { int l,r,c; scanf("%d%d%d",&l,&r,&c); update((l+1)*2,(r+1)*2,c+1,1,mm,1); //for(int i = 1;i <= 10;i++) printf("%d-",query(i,1,10,1)); //cout<<endl; } map<int,int> mp; int pre = query(1,1,mm,1); mp[pre]++; for(int i = 1;i <= mm;i++) { int tmp = query(i,1,mm,1); //printf("%d-",tmp); if(tmp != pre) mp[tmp]++; pre = tmp; } map<int,int>::iterator ii; for(ii = mp.begin();ii != mp.end();ii++) { pii it = (*ii); if(!it.fir) continue; printf("%d %d\n",it.fir-1,it.se); } printf("\n"); } }
HDU - 3974 J - Assign the task
题意:
给你n个村庄和m次操作。一开始n个村庄从左到右一排连在一起
也就是:1-2-3-4-5-6-7
D代表把一个村庄摧毁,比如D 3之后变成了:
1-2 3 4-5-6-7
R表示把最近的一次摧毁的村庄恢复
Q x代表询问x村庄和几个村庄相连
题解:
用线段树维护两个数组,R[maxn]和R[maxn],
R[pos]代表这个村庄可以到达的最右边的那个村庄,
L[pos]代表这个村庄可以到达的最左边的那个村庄。
每次更新两个数组即可。
比如样例就是:
7 9
1 1 1 1 1 1 1 –7 7 7 7 7 7 7
D 3
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
D 6
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
D 5
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
Q 4
1
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
Q 5
0
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
R
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
Q 4
2
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
R
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
Q 4
4
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn =1e5+5;const int inf = 0x3f3f3f3f;LL mod = 1e9+7;double eps = 0.00000001;int L[maxn<<2],R[maxn<<2],m1[maxn<<2],m2[maxn<<2],n,m;void build(int l,int r,int root) { if(l == r) { L[root] = 1; R[root] = n; return ; } int m = (l+r)>>1; build(l,m,root<<1); build(m+1,r,root<<1|1);}void push_down_l(int root) { L[root<<1] = m1[root]; L[root<<1|1] = m1[root]; m1[root<<1] = m1[root]; m1[root<<1|1] = m1[root]; m1[root] = 0;}void update_l(int left,int right,int num,int l,int r,int root) { if(left <= l && r <= right) { L[root] = num; m1[root] = num; return ; } if(m1[root]) push_down_l(root); int m = (l+r)>>1; if(m >= left) update_l(left,right,num,l,m,root<<1); if(m < right) update_l(left,right,num,m+1,r,root<<1|1);}int query_l(int pos,int l,int r,int root) { if(l == r && pos == l) { if(m1[root]) return m1[root]; else return L[root]; } if(m1[root]) push_down_l(root); int m = (l+r)>>1; if(m >= pos) query_l(pos,l,m,root<<1); else query_l(pos,m+1,r,root<<1|1);}void push_down_r(int root) { R[root<<1] = m2[root]; R[root<<1|1] = m2[root]; m2[root<<1] = m2[root]; m2[root<<1|1] = m2[root]; m2[root] = 0;}void update_r(int left,int right,int num,int l,int r,int root) { if(left <= l && r <= right) { R[root] = num; m2[root] = num; return ; } if(m2[root]) push_down_r(root); int m = (l+r)>>1; if(m >= left) update_r(left,right,num,l,m,root<<1); if(m < right) update_r(left,right,num,m+1,r,root<<1|1);}int query_r(int pos,int l,int r,int root) { if(l == r && pos == l) { if(m2[root]) return m2[root]; else return R[root]; } if(m2[root]) push_down_r(root); int m = (l+r)>>1; if(m >= pos) query_r(pos,l,m,root<<1); else query_r(pos,m+1,r,root<<1|1);}int main() { while(scanf("%d%d",&n,&m) != EOF) { build(1,n,1); stack<int> q; clr(m1,0); clr(m2,0); /*for(int i = 1;i <= n;i++) printf("%d ",query_l(i,1,n,1)); printf("--"); for(int i = 1;i <= n;i++) printf("%d ",query_r(i,1,n,1)); cout<<endl;*/ for(int i = 1;i <= m;i++) { char str[2];int pos; scanf("%s",str); if(str[0] == 'D') { scanf("%d",&pos); int ll = query_l(pos,1,n,1),rr = query_r(pos,1,n,1); if(pos <= rr) update_l(pos,rr,pos+1,1,n,1); if(pos >= ll) update_r(ll,pos,pos-1,1,n,1); q.push(pos); } else if(str[0] == 'R') { if(q.empty()) continue; pos = q.top();q.pop(); int ll,rr; if(pos > 1) ll = query_l(pos-1,1,n,1); else ll = 1; if(pos < n) rr = query_r(pos+1,1,n,1); else rr = n; if(pos <= rr) update_l(pos,rr,ll,1,n,1); if(pos >= ll) update_r(ll,pos,rr,1,n,1); } else { scanf("%d",&pos); int ll = query_l(pos,1,n,1),rr = query_r(pos,1,n,1); int ans = rr-ll+1; if(ans <= 0) ans = 0; printf("%d\n",ans); } /*for(int i = 1;i <= n;i++) printf("%d ",query_l(i,1,n,1)); printf("--"); for(int i = 1;i <= n;i++) printf("%d ",query_r(i,1,n,1)); cout<<endl;*/ } }}
HDU - 4578 K - Transformation
题意:
很裸的线段树,题意也很明显。不再赘述
题解:
这道题目一共有四种操作和三种询问,加在一起就很麻烦。
首先要知道push_down操作的顺序。
先更新op = 3,也就是替换操作。因为替换操作后之前的所有操作都不算了,也就是可以直接往下推,把op = 1和op = 2的标记清空。
再更新op = 2,也就是乘操作。因为更新完替换操作之后,乘操作也可以直接往下推,同时更新下面的加标记和乘标记。
最后更新op = 1,也就是加操作。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn =1e5+5;const int inf = 0x3f3f3f3f;LL mod = 10007;double eps = 0.00000001;struct node { LL sum1,sum2,sum3; int mark1,mark2,mark3;} rt[maxn<<2];void push_up(int root) { rt[root].sum1 = (rt[root<<1].sum1+rt[root<<1|1].sum1)%mod; rt[root].sum2 = (rt[root<<1].sum2+rt[root<<1|1].sum2)%mod; rt[root].sum3 = (rt[root<<1].sum3+rt[root<<1|1].sum3)%mod;}void push_down(int root,int cl,int cr) { if(rt[root].mark1) { //这里先更新替换操作,因为替换操作同时也可以直接更新乘和加操作 LL c = rt[root].mark1; rt[root<<1].mark1 = rt[root<<1|1].mark1 = c; rt[root<<1].mark3 = rt[root<<1|1].mark3 = 0; rt[root<<1].mark2 = rt[root<<1|1].mark2 = 1; rt[root].mark1 = 0; rt[root<<1].sum1 = cl * c % mod; rt[root<<1].sum2 = cl * c * c % mod; rt[root<<1].sum3 = cl * c * c * c % mod; rt[root<<1|1].sum1 = cr * c % mod; rt[root<<1|1].sum2 = cr * c * c % mod; rt[root<<1|1].sum3 = cr * c * c * c % mod; } if(rt[root].mark2 != 1) { LL c = rt[root].mark2; rt[root<<1].mark2 = rt[root<<1].mark2 * c % mod; //更新子树的乘和加的标记 rt[root<<1].mark3 = rt[root<<1].mark3 * c % mod; rt[root<<1|1].mark2 = rt[root<<1|1].mark2 * c % mod; rt[root<<1|1].mark3 = rt[root<<1|1].mark3 * c % mod; rt[root].mark2 = 1; rt[root<<1].sum1 = rt[root<<1].sum1 * c % mod; //更新子树的乘和加的和 rt[root<<1].sum2 = rt[root<<1].sum2 * c * c % mod; rt[root<<1].sum3 = rt[root<<1].sum3 * c * c * c % mod; rt[root<<1|1].sum1 = rt[root<<1|1].sum1 * c % mod; rt[root<<1|1].sum2 = rt[root<<1|1].sum2 * c * c % mod; rt[root<<1|1].sum3 = rt[root<<1|1].sum3 * c * c * c % mod; } if(rt[root].mark3) { LL c = rt[root].mark3; rt[root<<1].mark3 = (rt[root<<1].mark3 + c) % mod; rt[root<<1|1].mark3 = (rt[root<<1|1].mark3 + c) % mod; rt[root].mark3 = 0; LL lx1 = rt[root<<1].sum1,lx2 = rt[root<<1].sum2,lx3 = rt[root<<1].sum3; LL rx1 = rt[root<<1|1].sum1,rx2 = rt[root<<1|1].sum2,rx3 = rt[root<<1|1].sum3; rt[root<<1].sum1 = (lx1 + cl*c) % mod; //更新子树的加的和 rt[root<<1].sum2 = (lx2 + 2*c*lx1 + cl*c*c) % mod; rt[root<<1].sum3 = (lx3 + 3*lx2*c + 3*lx1*c*c + cl*c*c*c) % mod; rt[root<<1|1].sum1 = (rx1 + cr*c) % mod; rt[root<<1|1].sum2 = (rx2 + 2*c*rx1 + cr*c*c) % mod; rt[root<<1|1].sum3 = (rx3 + 3*rx2*c + 3*rx1*c*c + cr*c*c*c) % mod; }} void build(int l,int r,int root) { if(l == r) { rt[root].sum1 = rt[root].sum2 = rt[root].sum3 = 0; rt[root].mark1 = rt[root].mark3 = 0; rt[root].mark2 = 1; return ; } int m = (l+r)>>1; build(l,m,root<<1); build(m+1,r,root<<1|1); rt[root].sum1 = rt[root].sum2 = rt[root].sum3 = 0; rt[root].mark1 = rt[root].mark3 = 0; rt[root].mark2 = 1;}void update_chg(int L,int R,int c,int l,int r,int root) { //更新替换操作 if(L <= l && r <= R) { LL dis = r-l+1; rt[root].sum1 = dis * c % mod; rt[root].sum2 = dis * c * c % mod; rt[root].sum3 = dis * c * c * c % mod; rt[root].mark1 = c; //因为替换了,其他的标记就可以直接清空 rt[root].mark2 = 1; rt[root].mark3 = 0; //printf("%d - %d --- %d\n",l,r,rt[root].sum1); return ; } int m = (l+r)>>1; push_down(root,m-l+1,r-m); if(m >= L) update_chg(L,R,c,l,m,root<<1); if(m < R) update_chg(L,R,c,m+1,r,root<<1|1); push_up(root);}void update_mul(int L,int R,int c,int l,int r,int root) { //更新乘操作 if(L <= l && r <= R) { LL x1 = rt[root].sum1,x2 = rt[root].sum2,x3 = rt[root].sum3; rt[root].sum3 = (x3 * c * c * c) % mod; rt[root].sum2 = (x2 * c * c) % mod; rt[root].sum1 = (x1 * c) % mod; rt[root].mark2 = rt[root].mark2 * c % mod; //如果之前已经有加操作了,就把它直接乘c rt[root].mark3 = rt[root].mark3 * c % mod; return ; } int m = (l+r)>>1; push_down(root,m-l+1,r-m); if(m >= L) update_mul(L,R,c,l,m,root<<1); if(m < R) update_mul(L,R,c,m+1,r,root<<1|1); push_up(root);}void update_add(int L,int R,int c,int l,int r,int root) { //更新加操作 if(L <= l && r <= R) { LL x1 = rt[root].sum1,x2 = rt[root].sum2,x3 = rt[root].sum3,dis = r-l+1; rt[root].sum3 = (x3 + 3*x2*c + 3*x1*c*c + dis*c*c*c) % mod; rt[root].sum2 = (x2 + 2*c*x1 + dis*c*c) % mod; rt[root].sum1 = (x1 + dis*c) % mod; rt[root].mark3 = (rt[root].mark3 + c) % mod; return ; } int m = (l+r)>>1; push_down(root,m-l+1,r-m); if(m >= L) update_add(L,R,c,l,m,root<<1); if(m < R) update_add(L,R,c,m+1,r,root<<1|1); push_up(root);}LL query(int L,int R,int c,int l,int r,int root) { if(L <= l && r <= R) { if(c == 1) return rt[root].sum1; if(c == 2) return rt[root].sum2; if(c == 3) return rt[root].sum3; } int m = (l+r)>>1; push_down(root,m-l+1,r-m); LL ans = 0; if(m >= L) ans += query(L,R,c,l,m,root<<1); if(m < R) ans += query(L,R,c,m+1,r,root<<1|1); return (ans%mod);}main() { //freopen("in.txt","r",stdin); //freopen("out1.txt","w",stdout); int n,m; while(scanf("%d%d",&n,&m) != EOF && (n || m)) { build(1,n,1); for(int i = 1;i <= m;i++) { int op,l,r,c; scanf("%d%d%d%d",&op,&l,&r,&c); if(op == 1) update_add(l,r,c,1,n,1); if(op == 2) update_mul(l,r,c,1,n,1); if(op == 3) update_chg(l,r,c,1,n,1); if(op == 4) printf("%I64d\n",query(l,r,c,1,n,1)%mod); //for(int i = 1;i <= n;i++) printf("%I64d ",query(i,i,1,1,n,1)); //cout<<endl; } }}/*5 43 1 3 53 1 2 83 4 5 33 2 2 7*/
HDU - 4614 L - Vases and Flowers (二分+线段树)
题意:
n个花瓶,m次操作。
op = 1,从pos = k开始一直到pos = n,给你x朵花,碰到空花瓶就插花进去,直到花插完或者到了pos = n,如果多的花就丢了。
op = 2,给你l,r,问你当前l ~ r之间的花瓶内有多少朵花,并且把l ~ r之间的花瓶全部清空
题解:
首先,op = 2的时候,直接查询l ~ r之间的sum就可以,然后再update区间l ~ r的花为0
之后是op = 1。因为每个花瓶只能放一朵花,那么我们查询某个区间的sum,再减去区间内的花瓶数,就可以知道这个区间可以放多少朵花。知道这个之后,我们先求出从k ~ n我们可以查多少朵花,也就是nn = min(x , n-k+1 - query(k,n,1,n,1))
,然后在区间k ~ n二分求两个点,刚好可以插一朵花和可以插nn朵花。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn =1e5+5;const int inf = 0x3f3f3f3f;LL mod = 10007;double eps = 0.00000001;int n,m,sum[maxn<<2],mark[maxn<<2];void push_up(int root) { sum[root] = sum[root<<1]+sum[root<<1|1];}void push_down(int root,int cl,int cr) { mark[root<<1] = mark[root]; mark[root<<1|1] = mark[root]; sum[root<<1] = cl*mark[root]; sum[root<<1|1] = cr*mark[root]; mark[root] = -1;}void build(int l,int r,int root) { if(l == r) { sum[root] = 0; mark[root] = -1; return ; } int m = (l+r)>>1; build(l,m,root<<1); build(m+1,r,root<<1|1); push_up(root); mark[root] = -1;}void update(int L,int R,int c,int l,int r,int root) { if(L <= l && r <= R) { sum[root] = (r-l+1)*c; mark[root] = c; return ; } int m = (l+r)>>1; if(mark[root] != -1) push_down(root,m-l+1,r-m); if(m >= L) update(L,R,c,l,m,root<<1); if(m < R) update(L,R,c,m+1,r,root<<1|1); push_up(root);}int query(int L,int R,int l,int r,int root) { if(L <= l && r <= R) { return sum[root]; } int m = (l+r)>>1; if(mark[root] != -1) push_down(root,m-l+1,r-m); int ans = 0; if(m >= L) ans += query(L,R,l,m,root<<1); if(m < R) ans += query(L,R,m+1,r,root<<1|1); push_up(root); return ans;}int get_pos(int s,int x) { int l = s,r = n; if(x == 1 && query(s,s,1,n,1) == 0) return s; while(l + 1 < r) { int m = (l+r)>>1; if(m-s+1 - query(s,m,1,n,1) >= x) r = m; else l = m; //printf("%d-%d--%d\n",l,r,x); } return r;}int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); build(1,n,1); for(int i = 1;i <= m;i++) { int op,l,r; scanf("%d%d%d",&op,&l,&r); l++,r; if(op == 1) { int cnt = query(l,n,1,n,1); //printf("%d++++",cnt); if(cnt == n-l+1) { puts("Can not put any one."); continue; } int nn = min(r,n-l+1-cnt); int ll = get_pos(l,1),rr = get_pos(l,nn); update(ll,rr,1,1,n,1); printf("%d %d\n",ll-1,rr-1); } else { r++; printf("%d\n",query(l,r,1,n,1)); update(l,r,0,1,n,1); } //for(int i = 1;i <= n;i++) printf("%d ",query(i,i,1,n,1)); //cout<<endl; } cout<<endl; }}
HDU - 4553 M - 约会安排(区间合并)
题意:
给你长度为T的时间,m次询问
每次询问有三种情况:
屌丝,分配最近的长度为x还没有占用一块连续区间给他
女神,也是分配最近的长度为x还没有占用一块连续区间给他,但是如果找不到区间,就再去无视屌丝的占用再查询一次。
清空L-R之间所有的时间占用。
题解:
用到线段树的区间合并思想,维护每个区间对于屌丝的还没有占用的长度,dl,dr,dm。dl是该区间左边连续的空区间,dr是该区间右边连续的空区间,dm是该区间最长的连续的空区间。
对于女神就是nl,nr和nm。再用一个mark当懒惰标记。因为每一种更新都可以直接覆盖之前的更新,所以就只要一个标记,用不同的数字表示即可。1表示屌丝,2
表示女神,3表示清空。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn =1e5+5;const int inf = 0x3f3f3f3f;LL mod = 10007;double eps = 0.00000001;int n,m;struct node { int dl,dr,dm,nl,nr,nm,mark;} rt[maxn<<2];void build(int l,int r,int root) { //建树,主要是初始化 if(l == r) { rt[root].dl = rt[root].dr = rt[root].dm = 1; rt[root].nl = rt[root].nr = rt[root].nm = 1; rt[root].mark = 0; return ; } int m = (l+r)>>1; build(l,m,root<<1); build(m+1,r,root<<1|1); rt[root].dl = rt[root].dr = rt[root].dm = r-l+1; rt[root].nl = rt[root].nr = rt[root].nm = r-l+1; rt[root].mark = 0;}void push_up(int root,int cl,int cr) { //区间合并 if(rt[root<<1].dl == cl) rt[root].dl = rt[root<<1].dl + rt[root<<1|1].dl; else rt[root].dl = rt[root<<1].dl; if(rt[root<<1|1].dr == cr) rt[root].dr = rt[root<<1].dr + rt[root<<1|1].dr; else rt[root].dr = rt[root<<1|1].dr; rt[root].dm = max((rt[root<<1].dr+rt[root<<1|1].dl) , max(rt[root<<1].dm,rt[root<<1|1].dm)); if(rt[root<<1].nl == cl) rt[root].nl = rt[root<<1].nl + rt[root<<1|1].nl; else rt[root].nl = rt[root<<1].nl; if(rt[root<<1|1].nr == cr) rt[root].nr = rt[root<<1].nr + rt[root<<1|1].nr; else rt[root].nr = rt[root<<1|1].nr; rt[root].nm = max((rt[root<<1].nr+rt[root<<1|1].nl) , max(rt[root<<1].nm,rt[root<<1|1].nm));}void push_down(int root,int cl,int cr) { //根据不同标记,下推结果 if(rt[root].mark == 1) { rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = 0; rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = cl; rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = 0; rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = cr; } if(rt[root].mark == 2) { rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = 0; rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = 0; rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = 0; rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = 0; } if(rt[root].mark == 3) { rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = cl; rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = cl; rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = cr; rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = cr; } rt[root<<1].mark = rt[root<<1|1].mark = rt[root].mark; rt[root].mark = 0;}void update(int L,int R,int op,int l,int r,int root) { if(L <= l && r <= R) { if(op == 1) { rt[root].dl = rt[root].dr = rt[root].dm = 0; rt[root].nl = rt[root].nr = rt[root].nm = r-l+1; } if(op == 2) { rt[root].dl = rt[root].dr = rt[root].dm = 0; rt[root].nl = rt[root].nr = rt[root].nm = 0; } if(op == 3) { rt[root].dl = rt[root].dr = rt[root].dm = r-l+1; rt[root].nl = rt[root].nr = rt[root].nm = r-l+1; } rt[root].mark = op; return ; } int m = (l+r)>>1; if(rt[root].mark) push_down(root,m-l+1,r-m); if(m >= L) update(L,R,op,l,m,root<<1); if(m < R) update(L,R,op,m+1,r,root<<1|1); push_up(root,m-l+1,r-m);}int query(int len,int op,int l,int r,int root) { //printf("%d--%d\n",l,r); if(l == r) return l; int m = (l+r)>>1; if(rt[root].mark) push_down(root,m-l+1,r-m); if(op == 1) { //printf("%d ++++ %d\n",rt[root<<1].dm,len); if(rt[root<<1].dm >= len) return query(len,op,l,m,root<<1); else if(rt[root<<1].dr + rt[root<<1|1].dl >= len) return m-rt[root<<1].dr+1; else return query(len,op,m+1,r,root<<1|1); } else if(op == 2) { if(rt[root<<1].nm >= len) return query(len,op,l,m,root<<1); else if(rt[root<<1].nr + rt[root<<1|1].nl >= len) return m-rt[root<<1].nr+1; else return query(len,op,m+1,r,root<<1|1); }}int main() { int t,tt = 0; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); build(1,n,1); printf("Case %d:\n",++tt); for(int i = 1;i <= m;i++) { char str[15]; int x,l,r; scanf("%s",str); if(str[0] == 'D') { scanf("%d",&x); if(rt[1].dm >= x) { int pos = query(x,1,1,n,1); update(pos,pos+x-1,1,1,n,1); printf("%d,let's fly\n",pos); } else puts("fly with yourself"); } else if(str[0] == 'N') { scanf("%d",&x); if(rt[1].dm >= x) { int pos = query(x,1,1,n,1); printf("%d,don't put my gezi\n",pos); update(pos,pos+x-1,2,1,n,1); } else if(rt[1].nm >= x) { int pos = query(x,2,1,n,1); printf("%d,don't put my gezi\n",pos); update(pos,pos+x-1,2,1,n,1); } else puts("wait for me"); } else { scanf("%d%d",&l,&r); update(l,r,3,1,n,1); puts("I am the hope of chinese chengxuyuan!!"); } } }}
HDU - 1542 P - Atlantis (扫描线)
题意:
一共有n个矩形,接下来n行,分别是第 i 个矩形的左下角和右上角坐标,问你这些矩形叠在一起的总面积是多少。
题解:
线段树扫描线模板题。
扫描线的思想是把所有的线都转化成横线,并且按高度排好序。每次把当前的高度所有有效的横线*高度就是面积。当前高度的有效横线的长度就用线段树维护,而且先要用map什么的离散化一下。不是很难的算法。
代码:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <stack>#include <bitset>#include <queue>#include <set>#include <map>#include <string>#include <algorithm>using namespace std;#define clr(a,b) memset(a,b,sizeof(a))#define pb(a) push_back(a)#define fir first#define se second#define LL long longtypedef pair<int,int> pii;typedef pair<LL,int> pli;typedef pair<LL,LL> pll;const int maxn = 205;const int inf = 0x3f3f3f3f;LL mod = 10007;double eps = 0.00000001;int n,mark[maxn<<2];double sum[maxn<<2],has[maxn];map<double,int> mp;struct node { double l,r,h; //横线的左边,右边和高度 int d; //d为1代表矩形底边,-1代表顶边 node(){} node(double x1,double x2,double h1,int dd):l(x1),r(x2),h(h1),d(dd){}} s[maxn];bool cmp(node a,node b) { return a.h < b.h;}void push_up(int l,int r,int root) { if(mark[root]) sum[root] = has[r+1] - has[l]; else if(l == r) sum[root] = 0; else sum[root] = sum[root<<1] + sum[root<<1|1];}void update(int L,int R,int d,int l,int r,int root) { if(L <= l && r <= R) { mark[root] += d; push_up(l,r,root); return ; } int m = (l+r)>>1; if(m >= L) update(L,R,d,l,m,root<<1); if(m < R) update(L,R,d,m+1,r,root<<1|1); push_up(l,r,root);}int main() { int tt = 0; while(scanf("%d",&n) != EOF && n) { mp.clear(); for(int i = 1;i <= n;i++) { double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); s[i*2-1] = node(x1,x2,y1,1); s[i*2] = node(x1,x2,y2,-1); //把点都化成横线的形式 mp[x1] = 1; mp[x2] = 1; //离散化横坐标 } int cnt = 0; has[0] = 0; for(auto it : mp) { //离散化并存到has数组里 double tmp = it.fir; mp[tmp] = ++cnt; has[cnt] = tmp; } sort(s+1,s+1+n*2,cmp); //按高度对横线排序 double ans = 0; clr(sum,0); clr(mark,0); for(int i = 1;i <= n*2;i++) { update(mp[s[i].l],mp[s[i].r]-1,s[i].d,1,cnt,1); //用线段树维护横线的长度。 ans += sum[1]*(s[i+1].h - s[i].h); } printf("Test case #%d\nTotal explored area: %.2f\n\n",++tt,ans); }}
- 线段树复习--[kuangbin带你飞] 线段树
- [kuangbin带你飞]专题七 线段树
- [kuangbin带你飞]专题七 线段树 H HDU4027
- [kuangbin带你飞]专题七 线段树 B
- [kuangbin带你飞]专题七 线段树 A
- [kuangbin带你飞]专题七 线段树 C
- [kuangbin带你飞]专题七 线段树 E
- [kuangbin带你飞]专题七 线段树 F
- 线段树开新坑:kuangbin带你飞
- [kuangbin带你飞]专题七 线段树 ABCDE 题解,持续更新
- [kuangbin带你飞]专题七 线段树 A HDU 1166
- [kuangbin带你飞]专题七 线段树 B HDU 1754
- [kuangbin带你飞]专题七 线段树 C POJ 3468
- [kuangbin带你飞]专题七 线段树 D POJ 2528
- [kuangbin带你飞]专题七 线段树 E HDU 1698
- [kuangbin带你飞]专题七 线段树 G POJ 3264
- [kuangbin带你飞]专题七 线段树 I HDU 1540
- [kuangbin带你飞]专题七 线段树 J HDU 3974
- 561. Array Partition I
- QQ-AR实物识别!香蕉扫一扫,解救小黄人?
- Jtree生成系统文件树
- 华为——求int型正整数在内存中存储时1的个数
- ArrList详细讲述
- 线段树复习--[kuangbin带你飞] 线段树
- TCP UDP
- 浮动 清除浮动
- Glog 介绍
- activemq 事务--遇到异常始终回滚
- java内存机制
- mui上拉加载插件+Vue
- android native crash的处理机制
- java.lang.Integer cannot be cast to java.lang.String