菜鸟系列——线段树
来源:互联网 发布:淘宝4钻店铺值多少钱 编辑:程序博客网 时间:2024/06/13 08:14
做回菜鸟,老老实实重新学起:
线段树
基本数据结构;
求和模版:
#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int sum[N<<2],add[N<<2];void pushUp(int rt) { sum[rt] = sum[rt<<1]+sum[rt<<1|1];}void pushDown(int l,int r,int rt) { if(add[rt]) { int m = (l+r)>>1; add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; sum[rt<<1] += (m-l+1)*add[rt]; sum[rt<<1|1] += (r-m)*add[rt]; add[rt] = 0; }}void updata(int l,int r,int rt,int ql,int qr,int val) { if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] += (r-l+1)*val; add[rt] += val; return; } pushDown(l,r,rt); int m = (l+r)>>1; if(ql<=m)updata(lson,ql,qr,val); if(qr>m)updata(rson,ql,qr,val); pushUp(rt);}void build(int l,int r,int rt) { add[rt]=0; if(l == r) { scanf("%d",&sum[rt]); return; } int m = (l+r)>>1; build(lson); build(rson); pushUp(rt);}int query(int l,int r,int rt,int ql,int qr) { if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown(l,r,rt); int m = (l+r)>>1; return query(lson,ql,qr)+query(rson,ql,qr);}
eg:
题目参考:http://blog.csdn.net/metalseed/article/details/8039326
HDU1166 敌兵布阵
http://acm.hdu.edu.cn/showproblem.php?pid=1166
题意:
求区间和,单点加和更新;
思路:
线段树区间求和模版题;
code:
#define N 51234#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1long long sum[N<<2],add2[N<<2];void pushUp(int rt){ sum[rt] = sum[rt<<1]+sum[rt<<1|1];}void pushDown2(int l,int r,int rt)//加和操作的延迟标记{ if(add2[rt]) { int m = l+r>>1; add2[rt<<1] += add2[rt]; add2[rt<<1|1] += add2[rt]; sum[rt<<1] += (m-l+1)*add2[rt]; sum[rt<<1|1] += (r-m)*add2[rt]; add2[rt] = 0; }}void update2(int l,int r,int rt,int ql,int qr,int val)//加和操作{ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] += (r-l+1)*val; add2[rt] += val; return; } pushDown2(l,r,rt); int m = l+r>>1; if(ql<=m)update2(lson,ql,qr,val); if(qr>m)update2(rson,ql,qr,val); pushUp(rt);}void build(int l,int r,int rt){ add2[rt]=0; if(l == r) { scanf("%lld",&sum[rt]); return; } int m = l+r>>1; build(lson); build(rson); pushUp(rt);}long long query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown2(l,r,rt); int m = l+r>>1; return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);}int main(){ int i,j,k,kk,t,x,y; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif scanf("%d",&k); kk=0; while(k--) { printf("Case %d:\n",++kk); scanf("%d",&n); build(root); while(scanf("%s",s) && s[0]!='E') { scanf("%d %d",&x,&y); if(s[0] == 'Q') printf("%d\n",query(root,x,y)); else if(s[0] == 'A') update2(root,x,x,y); else update2(root,x,x,-y); } } return 0;}
HDU1754 I hate it
http://acm.hdu.edu.cn/showproblem.php?pid=1754
题意:
求区间最大值,单点更新;
思路:
线段树区间求最值模版题;
code:
#define N 200000#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int sum[N<<2],add1[N<<2];void pushUp(int rt){ sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);}void pushDown1(int l,int r,int rt)//更新操作的延迟标记{ if(add1[rt]) { int m = l+r>>1; add1[rt<<1] = add1[rt<<1|1] = add1[rt]; sum[rt<<1] = max(add1[rt],sum[rt<<1]); sum[rt<<1|1] = max(add1[rt],sum[rt<<1|1]); add1[rt] = 0; }}void update1(int l,int r,int rt,int ql,int qr,int val)//更新操作{ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] = max(sum[rt],val); add1[rt] = val; return; } pushDown1(l,r,rt); int m = l+r>>1; if(ql<=m)update1(lson,ql,qr,val); if(qr>m)update1(rson,ql,qr,val); pushUp(rt);}void build(int l,int r,int rt){ add1[rt]=0; if(l == r) { scanf("%lld",&sum[rt]); return; } int m = l+r>>1; build(lson); build(rson); pushUp(rt);}long long query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown1(l,r,rt); int m = l+r>>1; return max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr));}int main(){ int i,j,k,kk,t,x,y; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif while(scanf("%d %d",&n,&m)!=EOF&&n) { build(root); for(i=0;i<m;i++) { scanf("%s %d %d",s,&x,&y); if(s[0] == 'Q') printf("%d\n",query(root,x,y)); else update1(root,x,x,y); } } return 0;}
HDU1394 Minimum Inversion Number
http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意:
求该数列把头移到尾的所有情况的逆序数最小值;
思路:
用线段树依次加入求出初始逆序数;
把第一个数移到最后的新逆序数就是:
原来逆序数加上第一个数后面大于的数,减去后面更小的数;
又因为数列为0到n-1,
res = res+(n-num[i]-1)-(num[i]);
改为1到n时为:
res = res+(n-num[i])-(num[i]-1);
code:
#define N 51234#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1long long sum[N<<2],add1[N<<2],num[N];//向上更新操作void pushUp(int rt){ sum[rt] = sum[rt<<1]+sum[rt<<1|1];}//更新操作的延迟标记void pushDown1(int l,int r,int rt){ if(add1[rt]) { int m = l+r>>1; add1[rt<<1] = add1[rt<<1|1] = add1[rt]; sum[rt<<1] = (m-l+1)*add1[rt]; sum[rt<<1|1] = (r-m)*add1[rt]; add1[rt] = 0; }}//区间更新操作void update1(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] = (r-l+1)*val; add1[rt] = val; return; } pushDown1(l,r,rt); int m = l+r>>1; if(ql<=m)update1(lson,ql,qr,val); if(qr>m)update1(rson,ql,qr,val); pushUp(rt);}//区间和询问long long query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown1(l,r,rt); int m = l+r>>1; return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);}int main(){ int i,j,k,kk,t,x,y,res,ans; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif while(scanf("%d",&n)!=EOF&&n) { memset(sum,0,sizeof(sum)); memset(add1,0,sizeof(add1)); res = 0; //依次加入,求出最初逆序数 for(i=1;i<=n;i++) { scanf("%d",&num[i]); num[i]+=1;//原来数是从0到n-1 res += query(root,num[i],n); update1(root,num[i],num[i],1); } ans = res; //num[i]从头移到尾,逆序数加上num[i]后面小于num[i]的数n-num[i]; //再减去num[i]后面大于num[i]的数num[i]-1; for(i=1;i<n;i++) { res = res+n-num[i]-num[i]+1; ans = min(ans,res); } printf("%d\n",ans); } return 0;}
HDU2795 Billboard
http://acm.hdu.edu.cn/showproblem.php?pid=2795
题意:
在一个board上放(1,w)的传单,求能放到的最高处;
思路:
把board竖过来存成线段树,对于每个传单找大于wi的位置,再减去wi;code:
#define N 212345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int sum[N<<2];//向上更新操作void pushUp(int rt){ sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);}//建树void build(int l,int r,int rt){ sum[rt] = w; if(l == r) return; int m = l+r>>1; build(lson); build(rson);}long long query(int l,int r,int rt,int x){ if(l == r) { //询问同时更新操作 sum[rt] -= x; return l; } int m = (l+r)>>1; int res = (sum[rt<<1]>=x)?query(lson,x):query(rson,x); pushUp(rt); return res;}int main(){ int i,j,k,kk,t,x,y,res,ans; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif while(scanf("%d%d%d",&n,&w,&m)!=EOF&&n) { if(n>m)//利用公告总数小于200000,否则mlt n = m; build(root); for(i=0;i<m;i++) { scanf("%d",&t); if(sum[1]<t) printf("-1\n"); else printf("%d\n",query(root,t)); } } return 0;}
HDU1698 Just a Hook
http://acm.hdu.edu.cn/showproblem.php?pid=1698
题意:
区间更新求总和;
思路:
直接线段树区间更新模版;code:
#define N 112345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int sum[N<<2],add1[N<<2];void pushUp(int rt){ sum[rt] = sum[rt<<1]+sum[rt<<1|1];}void pushDown1(int l,int r,int rt){ if(add1[rt]) { int m = (l+r)>>1; add1[rt<<1] = add1[rt<<1|1] = add1[rt]; sum[rt<<1] = (m-l+1)*add1[rt]; sum[rt<<1|1] = (r-m)*add1[rt]; add1[rt] = 0; }}void update1(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] = (r-l+1)*val; add1[rt] = val; return; } pushDown1(l,r,rt); int m = (l+r)>>1; if(ql<=m)update1(lson,ql,qr,val); if(qr>m)update1(rson,ql,qr,val); pushUp(rt);}//建树void build(int l,int r,int rt){ add1[rt]=0; if(l == r) { sum[rt] = 1; return; } int m = (l+r)>>1; build(lson); build(rson); pushUp(rt);}//区间和询问int query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown1(l,r,rt); int m = l+r>>1; return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);}int main(){ int i,j,k,kk,t,x,y,res,ans; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif scanf("%d",&k); kk=0; while(k--) { scanf("%d%d",&n,&m); build(root); for(i=0;i<m;i++) { scanf("%d%d%d",&x,&y,&t); update1(root,x,y,t); } printf("Case %d: The total value of the hook is %d.\n",++kk,sum[1]); } return 0;}
POJ3468 A Simple Problem with Integers
http://poj.org/problem?id=3468
题意:
区间加和求总和;
思路:
直接线段树区间更新模版;code:
#define N 512345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1long long sum[N<<2],add2[N<<2];//向上更新操作void pushUp(int rt){ sum[rt] = sum[rt<<1]+sum[rt<<1|1];}//加和操作的延迟标记void pushDown2(int l,int r,int rt){ if(add2[rt]) { int m = (l+r)>>1; add2[rt<<1] += add2[rt]; add2[rt<<1|1] += add2[rt]; sum[rt<<1] += (m-l+1)*add2[rt]; sum[rt<<1|1] += (r-m)*add2[rt]; add2[rt] = 0; }}//区间整体加和操作void update2(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] += (r-l+1)*val; add2[rt] += val; return; } pushDown2(l,r,rt); int m = (l+r)>>1; if(ql<=m)update2(lson,ql,qr,val); if(qr>m)update2(rson,ql,qr,val); pushUp(rt);}//建树void build(int l,int r,int rt){ add2[rt]=0; if(l == r) { scanf("%lld",&sum[rt]); return; } int m = (l+r)>>1; build(lson); build(rson); pushUp(rt);}//区间和询问long long query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown2(l,r,rt); int m = l+r>>1; return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);}int main(){ int i,j,k,kk,t,x,y,res,ans; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif while(scanf("%d%d",&n,&m)!=EOF&&n) { build(root); for(i=0;i<m;i++) { scanf("%s",s); if(s[0]=='C') { scanf("%d%d%d",&x,&y,&t); update2(root,x,y,t); } else { scanf("%d%d",&x,&y); printf("%lld\n",query(root,x,y)); } } } return 0;}
POJ2528 Mayor’s posters
http://poj.org/problem?id=2528
题意:
贴海报,最后求还有几个海报能漏出来;
思路:
线段树区间更新,加上坐标离散化;但有个BUG,直接离散化会使相邻但不紧挨的海报合并,,应该在其中加一位;
但是数据奇弱能直接过,离散化加了考虑反而RE,,具体原因不详,再议
code:
#define N 112345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int n,m;int f[N][2];struct node{ int x,y; int r,w; int to,next; void init() { r=0; } friend bool operator < (node a, node b) {// if(a.y==b.y)return a.x > b.x; return a.x < b.x; }}tn[2*N];int sum[N<<2],mark[N],res;//向下更新操作void pushDown(int rt){ if(sum[rt]>0) { sum[rt<<1] = sum[rt<<1|1] = sum[rt]; sum[rt] = 0; }}//区间更新操作void update1(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql && r<=qr) { sum[rt] = val; return; } pushDown(rt); int m = (l+r)>>1; if(ql<=m)update1(lson,ql,qr,val); if(qr>m)update1(rson,ql,qr,val);}//建树void build(int l,int r,int rt){ sum[rt] = 0; if(l == r) { return; } int m = (l+r)>>1; build(lson); build(rson);}//区间询问void query(int l,int r,int rt){ if(sum[rt]>0) { if(mark[sum[rt]]==0) res++; mark[sum[rt]]=1; return; } if(l==r) return; query(lson); query(rson);}void discretize(int j){ sort(tn,tn+j); int now=1; int t = tn[0].x; for(int i=0;i<j;i++) { if(tn[i].x!=t) now++;// if(tn[i].x-t>1)// now++; t = tn[i].x; tn[i].x = now; if(tn[i].y>0) f[tn[i].y][0] = tn[i].x; else f[-tn[i].y][1] = tn[i].x; } n=now;}int main(){ int i,j,k,kk,t,x,y; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif scanf("%d",&k); while(k--) { int hehe; scanf("%d",&hehe); for(i=1,j=0;i<=hehe;i++) { scanf("%d%d",&f[i][0],&f[i][1]); tn[j].x = f[i][0]; tn[j++].y = i; tn[j].x = f[i][1]; tn[j++].y = -i; } discretize(j); build(root); for(i=1;i<=hehe;i++) update1(root,min(f[i][0],f[i][1]),max(f[i][0],f[i][1]),i); memset(mark,0,sizeof(mark)); res=0; query(root); printf("%d\n",res); } return 0;}
POJ3225 Help with Intervals
http://poj.org/problem?id=3225
题意:
区间操作
最后求在区间S中的所有区间
思路:
也就是:
U [l,r] = 1;
I [0,l) = (r,N] = 0;
D [l,r] = 0;
C [l,r] ^= 1;
用线段树操作,延迟标记好异或情况就行了;
主要是情况多,,细节!细节!细节!
code:
#define N 512345#define root 0 , N , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int sum[N<<2],OR[N<<2];bool mark[N+10];//做异或操作void doOR(int rt){ if(sum[rt] != -1)sum[rt] ^= 1; else OR[rt] ^= 1; //改点无色则延迟标记,向下推}//更新操作的延迟标记void pushDown(int l,int r,int rt){ if(sum[rt] != -1) { sum[rt<<1] = sum[rt<<1|1] = sum[rt]; OR[rt<<1] = OR[rt<<1|1] = 0; sum[rt] = -1; } if(OR[rt]) { doOR(rt<<1); doOR(rt<<1|1); OR[rt] = 0; }}//区间更新操作void update(int l,int r,int rt,int ql,int qr,char val){ if(l>=ql && r<=qr) { if(val == 'U') { sum[rt] = 1; OR[rt] = 0; } else if(val == 'D') { sum[rt] = 0; OR[rt] = 0; } else if(val == 'C' || val == 'S') doOR(rt); return; } pushDown(l,r,rt); int m = (l+r)>>1; if(ql<=m)update(lson,ql,qr,val); else if(val == 'I' || val == 'C') OR[rt<<1] = sum[rt<<1] = 0; if(qr>m)update(rson,ql,qr,val); else if(val == 'I' || val == 'C') OR[rt<<1|1] = sum[rt<<1|1] = 0;}//区间询问void query(int l,int r,int rt){ if(sum[rt] == 1) { for(int i=l;i<=r;i++) mark[i] = true; return; } else if(sum[rt] == 0) return ; if(l == r)return ; pushDown(l,r,rt); int m = (l+r)>>1; query(lson); query(rson);}int main(){ int i,j,k,kk,t,x,y; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif //更新每一条信息; sum[1] = OR[1] = 0; char c,l,r; while(scanf("%c %c%d,%d%c\n",&c,&l,&x,&y,&r)!=EOF) { //放大二倍,确定(与[; x*=2; y*=2; if(l == '(')x++; if(r == ')')y--; if(x>y && (c=='C'||c=='I'))//整个区间都在范围外全部清空; sum[1] = OR[1] = 0; else update(root,x,y,c); } //确定区间 memset(mark,false,sizeof(mark)); query(root); //输出 bool flag = false; x = y = -1; for(i=0;i<N;i++) { if(mark[i]) { if(x == -1) x = i; y = i; } else { if(x != -1) { if(flag)printf(" "); printf("%c%d,%d%c",x&1?'(':'[',x>>1,(y+1)>>1,y&1?')':']'); flag = true; x = y = -1; } } } if(!flag) printf("empty set"); printf("\n"); return 0;}
POJ3667 Hotel
http://poj.org/problem?id=3667
题意:
两类操作:
1 a 寻找是否有连续长度为a的空房
2 a b 将a房间开始的b个房间清空
对于1 操作输出连续空房的最左边的房间
思路:
线段树更新,线段树区间合并操作code:
#define N 51234#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int sum[N<<2],lsum[N<<2],rsum[N<<2],add1[N<<2];//向上更新操作void pushUp(int l,int r,int rt){ lsum[rt] = lsum[rt<<1]; rsum[rt] = rsum[rt<<1|1]; int m = r-l+1; if(lsum[rt] == m-(m>>1)) lsum[rt] += lsum[rt<<1|1]; if(rsum[rt] == (m>>1)) rsum[rt] += rsum[rt<<1]; sum[rt] = max(lsum[rt<<1|1]+rsum[rt<<1],max(sum[rt<<1],sum[rt<<1|1]));}//更新操作的延迟标记void pushDown(int l,int r,int rt){ if(add1[rt]!=-1) { int m = r-l+1; add1[rt<<1] = add1[rt<<1|1] = add1[rt]; sum[rt<<1] = lsum[rt<<1] = rsum[rt<<1] = add1[rt]?0:m-(m>>1); sum[rt<<1|1] = lsum[rt<<1|1] = rsum[rt<<1|1] = add1[rt]?0:(m>>1); add1[rt] = -1; }}//区间更新操作void update(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] = lsum[rt] = rsum[rt] = val?0:r-l+1; add1[rt] = val; return; } pushDown(l,r,rt); int m = (l+r)>>1; if(ql<=m)update(lson,ql,qr,val); if(qr>m)update(rson,ql,qr,val); pushUp(l,r,rt);}//建树void build(int l,int r,int rt){ add1[rt]=-1; sum[rt] = lsum[rt] = rsum[rt] = r-l+1; if(l == r) return; int m = (l+r)>>1; build(lson); build(rson);}//区间询问int query(int l,int r,int rt,int val){ if(l == r) return l; pushDown(l,r,rt); int m = l+r>>1; if(sum[rt<<1] >= val) return query(lson,val); else if(rsum[rt<<1]+lsum[rt<<1|1] >= val) return m-rsum[rt<<1]+1; return query(rson,val);}int main(){ int i,j,k,kk,t,x,y,op,n,m; #ifndef ONLINE_JUDGE freopen("test.txt","r",stdin); #endif while(scanf("%d%d",&n,&m)!=EOF&&n) { build(root); while(m--) { scanf("%d",&op); if(op == 1) { scanf("%d",&x); if(sum[1]<x) printf("0\n"); else { int res = query(root,x); printf("%d\n",res); update(root,res,res+x-1,1); } } else { scanf("%d %d",&x,&y); update(root,x,x+y-1,0); } } } return 0;}
HDU 4417 Super Mario
http://acm.hdu.edu.cn/showproblem.php?pid=4417
题意:
就是给出一组数,求在每一组询问区间中小于给定数值的数量。思路:
离线处理所有询问,对数组排序,依次加入,同时线段树求出加入该点前询问中区间的数量和就是对应答案。code:
#define N 112345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int n,m;struct node{ int x,y; int r,w; int to,next; friend bool operator < (node a, node b) { return a.r < b.r; }}a[N],b[N];bool cmp(node x,node y){return x.to < y.to;}int sum[N<<2],add2[N<<2];void pushUp(int rt){ sum[rt] = sum[rt<<1]+sum[rt<<1|1];}void pushDown2(int l,int r,int rt){ if(add2[rt]) { int m = (l+r)>>1; add2[rt<<1] += add2[rt]; add2[rt<<1|1] += add2[rt]; sum[rt<<1] += (m-l+1)*add2[rt]; sum[rt<<1|1] += (r-m)*add2[rt]; add2[rt] = 0; }}void update2(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] += (r-l+1)*val; add2[rt] += val; return; } pushDown2(l,r,rt); int m = (l+r)>>1; if(ql<=m)update2(lson,ql,qr,val); if(qr>m)update2(rson,ql,qr,val); pushUp(rt);}void build(int l,int r,int rt){ add2[rt]=0; if(l == r) { sum[rt]=0; return; } int m = (l+r)>>1; build(lson); build(rson); pushUp(rt);}int query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; pushDown2(l,r,rt); int m = l+r>>1; return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);}int main(){ int i,j,k,kk,t,x,y,z; scanf("%d",&k); kk=0; while(k--) { printf("Case %d:\n",++kk); scanf("%d%d",&n,&m); for(i=0;i<n;i++) { scanf("%d",&a[i].r); a[i].x=i+1; } for(i=0;i<m;i++) { scanf("%d %d %d",&b[i].x,&b[i].y,&b[i].r); b[i].to=i; } sort(a,a+n); sort(b,b+m); build(root); for(i=0,j=0;i<m;i++) { x=b[i].x+1;y=b[i].y+1;z=b[i].r; while(a[j].r<=z&&j<n) { update2(root,a[j].x,a[j].x,1); j++; } b[i].w=query(root,x,y); } sort(b,b+m,cmp); for(i=0;i<m;i++) printf("%d\n",b[i].w); } return 0;}
HDU 4521 小明序列
http://acm.hdu.edu.cn/showproblem.php?pid=4521
题意:
中文题,良心。给出一组数求其中的最长上升子序列,要求每两个数之间距离至少大于d。
思路:
数组升序排列,依次加入,该点的值就是1到Xi-d之间的最大值+1,就相当于是在该数前面的所有比它小的数中的最长上升子序列加上该数。注意相同的数要降序加入更新,防止相同的数重复加和。
code:
#define N 112345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int n,m;int flag,ave,ans,res;struct node{ int x,y; int r,w; int to; friend bool operator < (node a, node b) { if(a.r==b.r)return a.x > b.x; return a.r < b.r; }}a[N];bool cmp(node x,node y){return x.to < y.to;}int sum[N<<2];void pushUp(int rt){ sum[rt] = max(sum[rt<<1],sum[rt<<1|1]);}void update2(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] = (r-l+1)*val; return; } int m = (l+r)>>1; if(ql<=m)update2(lson,ql,qr,val); if(qr>m)update2(rson,ql,qr,val); pushUp(rt);}void build(int l,int r,int rt){ if(l == r) { sum[rt]=0; return; } int m = (l+r)>>1; build(lson); build(rson); pushUp(rt);}int query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; int m = l+r>>1; return max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr));}int main(){ int i,j,k,kk,t,x,y,z; while(scanf("%d%d",&n,&m)!=EOF&&n) { for(i=0;i<n;i++) { scanf("%d",&a[i].r); a[i].x=i+1; } sort(a,a+n); build(root); res=1; for(i=0;i<n;i++) { x=a[i].x; ans=0; if(x-m>0) { ans=query(root,1,x-m-1)+1; update2(root,x,x,ans); res=max(res,ans); } else update2(root,x,x,1); } printf("%d\n",res); } return 0;}
HDU 3308 LCIS
http://acm.hdu.edu.cn/showproblem.php?pid=3308
题意:
求数列中的最长连续上升子序列;有数字更新和区间询问两种操作。
思路:
线段树区间合并,与Hotel那题相似,开三个数组存储线段中最大序列和左右边界最大序列。再开两个数组储存每个节点左右端点的大小。合并时如果左儿子的右端点小于右儿子的左端点则将他们的中间共有区间加入判断。
询问时注意如果要把共有区间加入判断时,要注意判断共有区间的端点位置,取询问区间和共有连续上升子序列的并集。
code:
#define N 112345#define root 1 , n , 1#define lson l , m , rt << 1#define rson m + 1 , r , rt << 1 | 1int n,m;int sum[N<<2],lsum[N<<2],rsum[N<<2],ls[N<<2],rs[N<<2],add1[N<<2];void pushUp(int l,int r,int rt){ sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); if(rs[rt<<1]<ls[rt<<1|1]) sum[rt]=max(sum[rt],rsum[rt<<1]+lsum[rt<<1|1]); int mid=r-l+1; if(lsum[rt<<1]==mid-mid/2&&rs[rt<<1]<ls[rt<<1|1]) lsum[rt]=lsum[rt<<1]+lsum[rt<<1|1]; else lsum[rt]=lsum[rt<<1]; if(mid/2==rsum[rt<<1|1]&&rs[rt<<1]<ls[rt<<1|1]) rsum[rt]=rsum[rt<<1]+rsum[rt<<1|1]; else rsum[rt]=rsum[rt<<1|1]; rs[rt]=rs[rt<<1|1]; ls[rt]=ls[rt<<1];}void update1(int l,int r,int rt,int ql,int qr,int val){ if(l>qr||ql>r)return; if(l>=ql&&r<=qr) { sum[rt] = lsum[rt] = rsum[rt] = 1; ls[rt] = rs[rt] = val; return; } int m = (l+r)>>1; if(ql<=m)update1(lson,ql,qr,val); if(qr>m)update1(rson,ql,qr,val); pushUp(l,r,rt);}void build(int l,int r,int rt){ add1[rt]=-1; if(l == r) { sum[rt]=lsum[rt]=rsum[rt]=1; scanf("%d",&rs[rt]); ls[rt] = rs[rt]; return; } int m = (l+r)>>1; build(lson); build(rson); pushUp(l,r,rt);}int query(int l,int r,int rt,int ql,int qr){ if(l>qr||ql>r) return 0; if(l>=ql&&r<=qr) return sum[rt]; int m = l+r>>1; if(rs[rt<<1]<ls[rt<<1|1]) return max(min(qr,m+lsum[rt<<1|1])-max(ql,m+1-rsum[rt<<1])+1,max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr))); return max(query(l,m,rt<<1,ql,qr),query(m+1,r,rt<<1|1,ql,qr));}int main(){ int i,j,k,kk,t,x,y,z; scanf("%d",&k); while(k--) { scanf("%d %d",&n,&m); build(root); for(i=0;i<m;i++) { scanf("%s",s); if(s[0]=='Q') { scanf("%d%d",&x,&y); x++;y++; printf("%d\n",query(root,x,y)); } else if(s[0]=='U') { scanf("%d%d",&x,&y); x++; update1(root,x,x,y); } } } return 0;}
- 菜鸟系列——线段树
- 菜鸟系列——划分树
- 菜鸟系列——字典树
- 菜鸟系列——最小生成树
- 菜鸟系列——KMP
- 菜鸟系列——搜索
- 菜鸟系列——Sparse Table
- 菜鸟系列——回文串
- 菜鸟系列——最短路
- 菜鸟系列——强连通分量
- 菜鸟系列——二分图匹配
- 菜鸟系列——置换群
- 菜鸟系列——polya计数法
- 菜鸟系列——欧拉函数
- 菜鸟系列——约瑟夫环
- 菜鸟系列——容斥原理
- 菜鸟系列——双连通分量
- 菜鸟系列——康托展开
- 09求500万以内的亲合数
- 聊聊 iOS 开发
- jsp <select>标签
- mongodb
- 构造器(引用与值传递)
- 菜鸟系列——线段树
- Spring ApplicationContext的应用
- 游戏开发学习笔记-3.基本图元绘制
- 黑马程序员——java基础
- LightOJ 1047 - Neighbor House 【DP】
- Leetcode[162]-Find Peak Element
- ubuntu add bridge network with libvirt
- setprecision(int n)等格式函数用法
- 弹性布局