线段树单点更新+成段更新(好)hdu4973(多校联合)
2014-09-04 14:49
399 查看
Online Judge
Online Exercise
Online Teaching
Online Contests
Exercise Author
F.A.Q
Hand In Hand
Online Acmers
Forum | Discuss
Statistical Charts
Problem Archive
Realtime Judge Status
Authors Ranklist
C/C++/Java Exams
ACM Steps
Go to Job
Contest LiveCast
ICPC@China
Best Coder beta
VIP | STD
Contests
Virtual Contests
DIY | Web-DIY beta
Recent Contests
lee
0(0)
Control
Panel
Sign
Out
BestCoder官方QQ群:385386683 欢迎加入~
寻人启事:2014级新生看过来!
A simple simulation problem.
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 632 Accepted Submission(s): 246
Problem Description
There are n types of cells in the lab, numbered from 1 to n. These cells are put in a queue, the i-th cell belongs to type i. Each time I can use mitogen to double the cells in the interval [l, r]. For instance, the original
queue is {1 2 3 3 4 5}, after using a mitogen in the interval [2, 5] the queue will be {1 2 2 3 3 3 3 4 4 5}. After some operations this queue could become very long, and I can’t figure out maximum count of cells of same type. Could you help me?
Input
The first line contains a single integer t (1 <= t <= 20), the number of test cases.
For each case, the first line contains 2 integers (1 <= n,m<= 50000) indicating the number of cell types and the number of operations.
For the following m lines, each line represents an operation. There are only two kinds of operations: Q and D. And the format is:
“Q l r”, query the maximum number of cells of same type in the interval [l, r];
“D l r”, double the cells in the interval [l, r];
(0 <= r – l <= 10^8, 1 <= l, r <= the number of all the cells)
Output
For each case, output the case number as shown. Then for each query "Q l r", print the maximum number of cells of same type in the interval [l, r].
Take the sample output for more details.
Sample Input
1 5 5 D 5 5 Q 5 6 D 2 3 D 1 2 Q 1 7
Sample Output
Case #1: 2 3
题意:有两种操作,一种是把区间(l,r)内的每个数字复制,另一种是查询区间(l,r)区间内数量最多的数有多少个
思路:线段树,维护sum区间一共有多少个数,maxv区间最多的数有多少个,setv表示区间被复制了几次,这样当查询区间(l,r)首先得到其在原来的位置是多少,然后对于端点的值进行单点更新,中间的值进行成段更新就可以了。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<vector> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #include<algorithm> using namespace std; const int maxn=50010; typedef long long LL; int n,m; struct IntervalTree { LL sum[maxn<<2]; LL maxv[maxn<<2]; int setv[maxn<<2]; void pushup(int o) { sum[o]=sum[o<<1]+sum[o<<1|1]; maxv[o]=max(maxv[o<<1],maxv[o<<1|1]); } void build(int o,int l,int r) { setv[o]=0; if(l==r) { sum[o]=maxv[o]=1; return; } int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); pushup(o); } int getID(int o,int l,int r,LL x) { if(l==r)return l; pushdown(o); int mid=(l+r)>>1; if(sum[o<<1]>=x)return getID(o<<1,l,mid,x); else return getID(o<<1|1,mid+1,r,x-sum[o<<1]); } void pushdown(int o) { if(setv[o]) { setv[o<<1]+=setv[o]; setv[o<<1|1]+=setv[o]; sum[o<<1]<<=setv[o]; maxv[o<<1]<<=setv[o]; sum[o<<1|1]<<=setv[o]; maxv[o<<1|1]<<=setv[o]; setv[o]=0; } } void update1(int o,int l,int r,int x,LL val) { if(l==r) { sum[o]+=val; maxv[o]+=val; return; } pushdown(o); int mid=(l+r)>>1; if(x<=mid)update1(o<<1,l,mid,x,val); else update1(o<<1|1,mid+1,r,x,val); pushup(o); } void update2(int o,int l,int r,int q1,int q2) { if(q1<=l&&r<=q2) { setv[o]++; sum[o]*=2; maxv[o]*=2; return ; } pushdown(o); int mid=(l+r)>>1; if(q1<=mid)update2(o<<1,l,mid,q1,q2); if(q2>mid)update2(o<<1|1,mid+1,r,q1,q2); pushup(o); } LL querysum(int o,int l,int r,int q1,int q2) { if(q1<=l&&r<=q2)return sum[o]; pushdown(o); int mid=(l+r)>>1; LL ans=0; if(q1<=mid)ans+=querysum(o<<1,l,mid,q1,q2); if(q2>mid)ans+=querysum(o<<1|1,mid+1,r,q1,q2); return ans; } LL query(int o,int l,int r ,int q1,int q2) { if(q1<=l&&r<=q2)return maxv[o]; int mid=(l+r)>>1; pushdown(o); LL ans=0; if(q1<=mid)ans=max(ans,query(o<<1,l,mid,q1,q2)); if(q2>mid)ans=max(ans,query(o<<1|1,mid+1,r,q1,q2)); return ans; } }tree; int main() { int T,cas=1; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); tree.build(1,1,n); printf("Case #%d:\n",cas++); while(m--) { LL l,r; char op[5]; scanf("%s%I64d%I64d",op,&l,&r); int id1=tree.getID(1,1,n,l); int id2=tree.getID(1,1,n,r); if(op[0]=='D') { if(id1==id2) { LL x=r-l+1; tree.update1(1,1,n,id1,x); } else//这里注意要先对右端点进行更新,因为如果先对左端点更新,右面的值会发生变化 { LL tmp=r-tree.querysum(1,1,n,1,id2-1); tree.update1(1,1,n,id2,tmp); tmp=tree.querysum(1,1,n,1,id1)-l+1; tree.update1(1,1,n,id1,tmp); int q1=id1+1,q2=id2-1; if(q1<=q2)tree.update2(1,1,n,q1,q2); } } else { if(id1==id2)printf("%I64d\n",r-l+1); else { LL ans=0; LL tmp=r-tree.querysum(1,1,n,1,id2-1); ans=max(ans,tmp); tmp=tree.querysum(1,1,n,1,id1)-l+1; ans=max(ans,tmp); int q1=id1+1,q2=id2-1; if(q1<=q2)ans=max(ans,tree.query(1,1,n,q1,q2)); printf("%I64d\n",ans); } } } } return 0; }
还有更简洁的写法
update的时候在[L,R]这些数里找排在第[ql,qr]的,如果sum[o<<1]>=qr,就在左区间找[ql,qr],如果sum[p<<1]>ql就在右区间找[ql-sum[o<<1]],qr-sum[o<<1]],否则就在左边找[ql,sum[o<<1]],右区间找[1,qr-sum[o<<1]]。查询原理也一样。
#include<iostream> #include<algorithm> #include<queue> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<stack> #define INF 0x3f3f3f3f #define MAXN 50010 #define MAXM 110 #define MOD 1000000007 #define MAXNODE 4*MAXN #define eps 1e-9 using namespace std; typedef long long LL; LL T,N,M; char op[10]; LL sum[MAXNODE],maxv[MAXNODE],mul[MAXNODE]; void maintain(LL o){ sum[o]=sum[o<<1]+sum[o<<1|1]; maxv[o]=max(maxv[o<<1],maxv[o<<1|1]); } void build(LL o,LL L,LL R){ mul[o]=0; if(L==R){ maxv[o]=sum[o]=1; return; } LL mid=(L+R)>>1; build(o<<1,L,mid); build(o<<1|1,mid+1,R); maintain(o); } void pushdown(LL o){ if(mul[o]){ LL lc=o<<1,rc=o<<1|1; sum[lc]<<=mul[o]; sum[rc]<<=mul[o]; maxv[lc]<<=mul[o]; maxv[rc]<<=mul[o]; mul[lc]+=mul[o]; mul[rc]+=mul[o]; mul[o]=0; } } void update(LL o,LL L,LL R,LL ql,LL qr){ if(L==R){ sum[o]+=qr-ql+1; maxv[o]=sum[o]; return; } if(sum[o]==qr-ql+1){ mul[o]++; sum[o]<<=1; maxv[o]<<=1; return; } pushdown(o); LL mid=(L+R)>>1; if(sum[o<<1]>=qr) update(o<<1,L,mid,ql,qr); else if(sum[o<<1]<ql) update(o<<1|1,mid+1,R,ql-sum[o<<1],qr-sum[o<<1]); else{ update(o<<1|1,mid+1,R,1,qr-sum[o<<1]); update(o<<1,L,mid,ql,sum[o<<1]); } maintain(o); } LL query(LL o,LL L,LL R,LL ql,LL qr){ if(L==R) return qr-ql+1; if(sum[o]==qr-ql+1) return maxv[o]; pushdown(o); LL mid=(L+R)>>1; if(sum[o<<1]>=qr) return query(o<<1,L,mid,ql,qr); else if(sum[o<<1]<ql) return query(o<<1|1,mid+1,R,ql-sum[o<<1],qr-sum[o<<1]); else return max(query(o<<1,L,mid,ql,sum[o<<1]),query(o<<1|1,mid+1,R,1,qr-sum[o<<1])); } int main(){ freopen("in.txt","r",stdin); scanf("%I64d",&T); LL cas=0; while(T--){ printf("Case #%I64d:\n",++cas); scanf("%I64d%I64d",&N,&M); build(1,1,N); while(M--){ LL ql,qr; scanf("%s",op); scanf("%I64d%I64d",&ql,&qr); if(op[0]=='D') update(1,1,N,ql,qr); else printf("%I64d\n",query(1,1,N,ql,qr)); } } return 0; }
相关文章推荐
- 牛客网NowCoder 2018年全国多校算法寒假训练营练习比赛(第五场)A.逆序数 B.Big Water Problem(线段树-区间查询求和和单点更新) F.The Biggest Water Problem H.Tree Recovery(线段树-区间查询求和和区间更新)
- 线段树(单点更新and成段更新)
- 线段树成段更新化为单点更新-杭电4027
- POJ 2155 Matrix (二维线段树入门,成段更新,单点查询 / 二维树状数组,区间更新,单点查询)
- hdu 5316 Magician(2015多校第三场第1题)线段树单点更新+区间合并
- hdu 5316 Magician(2015多校第三场第1题)线段树单点更新+区间合并
- hdu 5316 Magician(2015多校第三场第1题)线段树单点更新+区间合并
- poj 2828 线段树 单点更新 代码很简单,主要在于思路
- 线段树单点更新 hdu 2795 Billboard
- HDU 3074 Multiply game(线段树 单点更新)
- poj 2892 Tunnel Warfare(线段树 单点更新 区间合并)
- POJ-2828 Buy Tickets【线段树 单点更新】
- hdu.3308 LCIS(线段树,区间合并+单点更新)
- HDU1166 线段树 裸 单点更新
- POJ-2481 Cows (线段树单点更新)
- HDU1166:敌兵布阵(线段树单点更新)
- HDU 1754 I Hate It(线段树,单点更新,线段查询)
- HDU 5493 Queue(线段树啊 单点更新)
- HDU2795:Billboard(线段树单点更新)
- HDOJ 1754 单点更新段查询最大值 初级线段树