hdu 1166 敌兵布阵
操作:单点增加或减少,查询区间和.
http://acm.hdu.edu.cn/showproblem.php?pid=1166
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=50010; struct ST { int l,r; int sum; }st[maxn<<2]; void pushUp(int i) { st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum; } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; if(st[i].l==st[i].r) { rd(st[i].sum); return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushUp(i); } void add(int i,int p,int val) { if(st[i].l==st[i].r) { st[i].sum+=val; return; } int mid=(st[i].l+st[i].r)>>1; if(p<=mid) add(i<<1,p,val); else add((i<<1)|1,p,val); pushUp(i); } int query(int i,int L,int R) { if(st[i].l==L&&st[i].r==R) { return st[i].sum; } int mid=(st[i].l+st[i].r)>>1; if(R<=mid) return query(i<<1,L,R); else if(L>mid) return query((i<<1)|1,L,R); else return query(i<<1,L,mid)+query((i<<1)|1,mid+1,R); } int n; char cm[10]; int main() { int cas=1; int t;rd(t); while(t--) { printf("Case %d:\n",cas++); rd(n); build(1,1,n); while(scanf("%s",cm)) { if(cm[0]=='Q') { int l,r; rd2(l,r); printf("%d\n",query(1,l,r)); } else if(cm[0]=='A') { int p,val; rd2(p,val); add(1,p,val); } else if(cm[0]=='S') { int p,val; rd2(p,val); add(1,p,-val); } else break; } } return 0; }
hdu 1754 I hate it
http://acm.hdu.edu.cn/showproblem.php?pid=1754
操作:单点替换为另一个值,查询区间最大值.
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=200010; struct ST { int l,r; int MAX; }st[maxn<<2]; void pushUp(int i) { st[i].MAX=max(st[i<<1].MAX,st[(i<<1)|1].MAX); } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; if(st[i].l==st[i].r) { rd(st[i].MAX); return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushUp(i); } void update(int i,int p,int val) { if(st[i].l==st[i].r) { st[i].MAX=val; return; } int mid=(st[i].l+st[i].r)>>1; if(p<=mid) update(i<<1,p,val); else update((i<<1)|1,p,val); pushUp(i); } int query(int i,int L,int R) { if(st[i].l==L&&st[i].r==R) { return st[i].MAX; } int mid=(st[i].l+st[i].r)>>1; if(R<=mid) return query(i<<1,L,R); else if(L>mid) return query((i<<1)|1,L,R); else return max(query(i<<1,L,mid),query((i<<1)|1,mid+1,R)); } int n,m; char cm[5]; int main() { while(rd2(n,m)!=EOF) { build(1,1,n); while(m--) { scanf("%s",cm); if(cm[0]=='Q') { int l,r; rd2(l,r); printf("%d\n",query(1,l,r)); } else { int p,val; rd2(p,val); update(1,p,val); } } } return 0; }
poj 3468 A Simple Problem with Integers
http://poj.org/problem?id=3468
操作:区间的每个值都增加一个数,查询区间总和。
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int maxn=100010; struct ST { int l,r; ll lazy; ll sum; }st[maxn<<2]; void pushUp(int i) { st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum; } void pushDown(int i,int len) { if(st[i].lazy!=0) { st[i<<1].lazy+=st[i].lazy; st[(i<<1)|1].lazy+=st[i].lazy; st[i<<1].sum+=(long long)(len-(len>>1))*st[i].lazy; st[(i<<1)|1].sum+=(long long)(len>>1)*st[i].lazy; st[i].lazy=0; } } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; st[i].lazy=0; if(st[i].l==st[i].r) { scanf("%I64d",&st[i].sum); return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushUp(i); } void add(int i,int L,int R,int val) { if(st[i].l==L&&st[i].r==R) { st[i].sum+=(long long)(st[i].r-st[i].l+1)*val; st[i].lazy+=val; return; } pushDown(i,st[i].r-st[i].l+1); int mid=(st[i].l+st[i].r)>>1; if(R<=mid) add(i<<1,L,R,val); else if(L>mid) add((i<<1)|1,L,R,val); else { add(i<<1,L,mid,val); add((i<<1)|1,mid+1,R,val); } pushUp(i); } ll query(int i,int L,int R) { if(st[i].l==L&&st[i].r==R) { return st[i].sum; } pushDown(i,st[i].r-st[i].l+1); int mid=(st[i].l+st[i].r)>>1; if(R<=mid) return query(i<<1,L,R); else if(L>mid) return query((i<<1)|1,L,R); else return query(i<<1,L,mid)+query((i<<1)|1,mid+1,R); } int n,q; int l,r,val; char cm; int main() { while(scanf("%d%d",&n,&q)!=EOF) { build(1,1,n); while(q--) { scanf("%s",&cm); if(cm=='C') { scanf("%d%d%d",&l,&r,&val); add(1,l,r,val); } else { scanf("%d%d",&l,&r); printf("%I64d\n",query(1,l,r)); } } } return 0; }
hdu 1698 just a hook
http://acm.hdu.edu.cn/showproblem.php?pid=1698
操作:指定区间每个值修改为另一个值,求总区间总和。
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int maxn=100010; struct ST { int l,r; int lazy; int sum; }st[maxn<<2]; void pushUp(int i) { st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum; } void pushDown(int i,int len) { if(st[i].lazy!=0) { st[i<<1].lazy=st[(i<<1)|1].lazy=st[i].lazy; st[i<<1].sum=(len-(len>>1))*st[i].lazy; st[(i<<1)|1].sum=(len>>1)*st[i].lazy; st[i].lazy=0; } } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; st[i].lazy=0; if(st[i].l==st[i].r) { st[i].sum=1; return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushUp(i); } void update(int i,int l,int r,int val) { if(st[i].l==l&&st[i].r==r) { st[i].sum=(st[i].r-st[i].l+1)*val; st[i].lazy=val; return; } pushDown(i,st[i].r-st[i].l+1); int mid=(st[i].l+st[i].r)>>1; if(r<=mid) update(i<<1,l,r,val); else if(l>mid) update((i<<1)|1,l,r,val); else { update(i<<1,l,mid,val); update((i<<1)|1,mid+1,r,val); } pushUp(i); } int t,n,q; int l,r,val; int cas=1; int main() { rd(t); while(t--) { rd(n); rd(q); build(1,1,n); while(q--) { rd3(l,r,val); update(1,l,r,val); } printf("Case %d: The total value of the hook is %d.\n",cas++,st[1].sum); } return 0; }
poj 2528 Mayor's posters
http://poj.org/problem?id=2528
有一条1到10000000的线段,然后给指定区间涂颜色,制定区间个数最多为10000个,后面的颜色覆盖前面的颜色,问最后涂完一共可以看见几种颜色。
要用到离散化,比如 [1, 10000] [2, 99999999] [10000,20000]这三个区间
端点从小到大排序并去重后得到
1 2 10000 20000 99999999 分别映射到数字 1 2 3 4 5,也就是1->1 2->2 10000->3 20000->4 99999999->5
那么给定的区间就可以变为[1,3] [2,5] [3,4] ,原覆盖关系没变,这样区间长度就大大缩短了
但这样容易出错 比如 [1,10] [1,4] [6,10] 这组数据,答案应该是3, 但是像前面那样处理后,区间变为[1,4] [1,2] [3,4] ,答案是2,处理方法为
端点从小到大排序并去重得到1 4 6 10 ,对于相邻两个数如果差距大于1,那么就再里面随便加上一个数,变为 1 (2) 4 (5) 6 (7) 10
然后映射关系为1->1 2->2 4->3 5->4 6->5 7->6 10->7
那么原区间就变为[1,7] [ 1,3] [5,7] ,答案正确为3
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=10010; int id[10000005];//离散化以后对应的id,原区间为l,r,离散化后变为id[l],id[r] int t; int n; int x[maxn*3];//4倍的,2倍是一条线段两个端点,1倍是两个端点之间再加一个,就像[1,10] [1,4] [6,10]这样的数据防止错误 struct ST { int l,r; int covered; }st[maxn*16];//要开线段长度的4倍,按理说12就可以,但是re,开了13就可以,迷糊..... struct poster { int l,r; }post[maxn]; void build(int i,int l,int r) { st[i].l=l; st[i].r=r; st[i].covered=false;//一开始都是没被覆盖 if(st[i].l==st[i].r) return; int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); } bool isvisible(int i,int l,int r) { if(st[i].covered==true)//如果该节点代表的区间已被覆盖掉,直接返回 return false; if(st[i].l==l&&st[i].r==r)//找到那一段区间 { st[i].covered=true; return true; } bool ok; int mid=(st[i].l+st[i].r)>>1; if(r<=mid) ok=isvisible(i<<1,l,r); else if(l>mid) ok=isvisible((i<<1)|1,l,r); else { bool a=isvisible(i<<1,l,mid);//左一半区间是否被覆盖 bool b=isvisible((i<<1)|1,mid+1,r);//有一半 ok=a||b;//有一个为真,Ok就为真,为真代表没有被覆盖 } if(st[i<<1].covered&&st[(i<<1)|1].covered)//向上更新 st[i].covered=true; return ok; } int main() { rd(t); while(t--) { rd(n); int len=0; for(int i=1;i<=n;i++) { rd2(post[i].l,post[i].r); x[len++]=post[i].l; x[len++]=post[i].r; } sort(x,x+len); len=unique(x,x+len)-x;//去掉重复 int tp=len; for(int i=1;i<tp;i++) { if(x[i]-x[i-1]>1) x[len++]=x[i-1]+1;//避免 [1,10] [1,4] [6,10]这样的数据 } sort(x,x+len); for(int i=0;i<len;i++) { id[x[i]]=i+1; } build(1,1,len); int ans=0; for(int i=n;i>=1;i--) { if(isvisible(1,id[post[i].l],id[post[i].r])) ans++; } printf("%d\n",ans); } return 0; }
zoj 1610 Count the Colors
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1610
在一条线段上染色,颜色的种类用数字表示,后染色的覆盖前面的染色,问最后能看到几种颜色,且每种颜色有多少不连续的段。比如 [1,8]先染成白色,[1,2]再染成红色,[5,8]再染成红色,那么可以看到白色的有一段(中间),红色的有两段。 题目中所有涉及的数的范围都为[0,8000]。
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=8010; int color[maxn];//每个点的颜色 int ans[maxn];//输出的 struct ST { int l,r; int flag;//代表的颜色,默认-1 }st[maxn<<2]; void pushUp(int i) { if(st[i<<1].flag==st[(i<<1)|1].flag&&st[i<<1].flag!=-1) st[i].flag=st[i<<1].flag; } void pushDown(int i) { if(st[i].flag!=-1) { st[i<<1].flag=st[i].flag; st[(i<<1)|1].flag=st[i].flag; st[i].flag=-1;//这里是-1,其实是混合色 } } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; st[i].flag=-1; if(st[i].l==st[i].r) return; int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); } void update(int i,int l,int r,int val) { if(st[i].flag==val) return; if(l<=st[i].l&&st[i].r<=r)//因为参数l,r是整个染色的区间,下面的递归也是整个染色的区间,用[1,9]建立线段树,给[1,8]染色模拟一遍就懂了 { st[i].flag=val; return; } pushDown(i); int mid=(st[i].l+st[i].r)>>1; if(r<=mid) update(i<<1,l,r,val); else if(l>mid) update((i<<1)|1,l,r,val); else { update(i<<1,l,r,val); update((i<<1)|1,l,r,val); } pushUp(i); } void query(int i) { if(st[i].flag!=-1) { for(int j=st[i].l;j<=st[i].r;j++) color[j]=st[i].flag; return; } if(st[i].l==st[i].r)//父节点的flag-1,那么子节点的flag有可能-1,也有可能不是-1,加这一句话是-1的情况,没有染色 return; query(i<<1); query((i<<1)|1); } int n,N=8000; int l,r,val; int main() { while(rd(n)!=EOF) { build(1,0,N); memset(color,-1,sizeof(color)); memset(ans,0,sizeof(ans)); while(n--) { rd3(l,r,val); update(1,l,r-1,val);//为了避免 [1,2] [3,4]这样的情况,如果是相同的颜色,那么应该是两段[2,3]是空白 } query(1); int pre=-1; for(int i=0;i<N;i++) { if(pre!=color[i]) { pre=color[i]; if(pre==-1) continue; ans[color[i]]++; } } for(int i=0;i<N;i++) if(ans[i]) printf("%d %d\n",i,ans[i]); printf("\n"); } return 0; }
poj 3264 Balanced Lineup
http://poj.org/problem?id=3264
求区间内最大值与最小值的差。
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=50010; struct ST { int l,r; int MAX,MIN; }st[maxn<<2]; void pushUp(int i) { st[i].MAX=max(st[i<<1].MAX,st[(i<<1)|1].MAX); st[i].MIN=min(st[i<<1].MIN,st[(i<<1)|1].MIN); } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; if(st[i].l==st[i].r) { rd(st[i].MAX); st[i].MIN=st[i].MAX; return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushUp(i); } int query1(int i,int l,int r) { if(st[i].l==l&&st[i].r==r) { return st[i].MAX; } int mid=(st[i].l+st[i].r)>>1; if(r<=mid) return query1(i<<1,l,r); else if(l>mid) return query1((i<<1)|1,l,r); else { return max(query1(i<<1,l,mid),query1((i<<1)|1,mid+1,r)); } } int query2(int i,int l,int r) { if(st[i].l==l&&st[i].r==r) { return st[i].MIN; } int mid=(st[i].l+st[i].r)>>1; if(r<=mid) return query2(i<<1,l,r); else if(l>mid) return query2((i<<1)|1,l,r); else { return min(query2(i<<1,l,mid),query2((i<<1)|1,mid+1,r)); } } int MAX,MIN; void query(int i,int l,int r) { if(st[i].MAX<MAX&&st[i].MIN>MIN) return; if(st[i].l==l&&st[i].r==r) { MAX=max(st[i].MAX,MAX); MIN=min(st[i].MIN,MIN); return; } int mid=(st[i].l+st[i].r)>>1; if(r<=mid) query(i<<1,l,r); else if(l>mid) query((i<<1)|1,l,r); else { query(i<<1,l,mid); query((i<<1)|1,mid+1,r); } } int n,q; int l,r; int main() { while(rd2(n,q)!=EOF) { build(1,1,n); while(q--) { rd2(l,r); MAX=-1,MIN=1000002; // printf("%d\n",query1(1,l,r)-query2(1,l,r));这个也可以 query(1,l,r); printf("%d\n",MAX-MIN); } } return 0; }
hdu 4027 Can you answer these queries?
http://acm.hdu.edu.cn/showproblem.php?pid=4027
操作:给定区间,将区间内的每个值都变为原来的平方根(去整),查询区间和。long long
当一个数为0或者为1时,该数就不用再被更新了,用allone表示该数或者该区间是否需要更新,当allone==1是,表示不用更新,碰到该节点,直接返回就可以。
题目给出的区间端点x,y,没有说明x<=y,得判断一下.....
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=100010; struct ST { int l,r; ll sum; bool allone; }st[maxn<<2]; void pushUp(int i) { st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum; st[i].allone=st[i<<1].allone&&st[(i<<1)|1].allone; } void build(int i,int l,int r) { st[i].l=l; st[i].r=r; if(st[i].l==st[i].r) { scanf("%I64d",&st[i].sum); if(st[i].sum==0||st[i].sum==1) st[i].allone=1; else st[i].allone=0; return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushUp(i); } void cal(int i,int l,int r) { //if(st[i].sum==(st[i].r-st[i].l+1))//数据中如果每个endurance值不为0的话可以这样判断,这个区间每个点都为1,就不用再更新 // return; if(st[i].allone==1)//该区间内全是1,不用再更新了 return; if(st[i].l==st[i].r)//找到该数 { st[i].sum=(ll)sqrt(1.0*st[i].sum); if(st[i].sum==1) st[i].allone=1; return; } int mid=(st[i].l+st[i].r)>>1; if(r<=mid) cal(i<<1,l,r); else if(l>mid) cal((i<<1)|1,l,r); else { cal(i<<1,l,mid); cal((i<<1)|1,mid+1,r); } pushUp(i); } ll query(int i,int l,int r) { if(st[i].l==l&&st[i].r==r) { return st[i].sum; } int mid=(st[i].l+st[i].r)>>1; if(r<=mid) return query(i<<1,l,r); else if(l>mid) return query((i<<1)|1,l,r); else { return query(i<<1,l,mid)+query((i<<1)|1,mid+1,r); } } int n,q; int cm,l,r; int c=1; int main() { while(rd(n)!=EOF) { printf("Case #%d:\n",c++); build(1,1,n); rd(q); while(q--) { rd3(cm,l,r); if(l>r) swap(l,r);//陷阱! if(!cm) { cal(1,l,r); } else printf("%I64d\n",query(1,l,r)); } printf("\n"); } return 0; }
poj 2892 Tunnel Warfare
http://poj.org/problem?id=2892
一开始有连续的n个1,位置分别是1-n,三种操作,一是将某个位置的1变为0,而是将某个位置的0修复为1,三是查询包含某个位置的最大连续区间(该区间内都是1)的长度.
样例
7 9
D 3
D 6
D 5
Q 4
Q 5
R
Q 4
R
Q 4
一开始7个位置全是1, 1 1 1 1 1 1 1, D 3表示将第3个位置设为0,即 1 1 0 1 1 1 1,D 6, 1 1 0 1 1 0 1,D 5 , 1 1 0 1 0 0 1,
Q 4表示查询包含位置4的最大连续区间(全为1), 答案为1,Q 5答案为0, 都是在 1 1 0 1 0 0 1中看出来的
R表示将最后一个设为0的位置修复为1,也就是将第5个位置修复为1 , 即 1 1 0 1 1 0 1,Q 4 ,答案为2
再一个R表示将第6个位置修复为1,1 1 0 1 1 1 1,Q4, 答案为4
想法在注释中。
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=50010; struct ST { int l,r; int ls,rs,ms;//区间左端多少个连续1,右端多少个连续1,该区间最大连续1长多少,即左端连续区间,右端连续区间,最大连续区间 }st[maxn<<2]; void build(int i,int l,int r) { st[i].l=l; st[i].r=r; if(st[i].l==st[i].r) { st[i].ls=1;st[i].rs=1;st[i].ms=1; return; } int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); st[i].ls=st[i].rs=st[i].ms=st[i].r-st[i].l+1; } void update(int i,int p,char c)//位置为p,命令为c { if(st[i].l==st[i].r) { if(c=='D')//破坏 st[i].ls=st[i].rs=st[i].ms=0; else//修复 st[i].ls=st[i].rs=st[i].ms=1; return; } int mid=(st[i].l+st[i].r)>>1; if(p<=mid) update(i<<1,p,c); else update((i<<1)|1,p,c); st[i].ls=st[i<<1].ls;//孩子节点左连续区间肯定是父节点的左端连续区间的一部分 st[i].rs=st[(i<<1)|1].rs;//右连续区间 st[i].ms=max(max(st[i<<1].ms,st[(i<<1)|1].ms),st[i<<1].rs+st[(i<<1)|1].ls); //父亲节点的最大连续区间为 左孩子最大连续区间,右孩子最大连续区间,中间连续最大区间三者中的最大值 //如果左孩子都是连续区间,那么父节点的左连续区间除了左孩子的左连续区间,还要加上右孩子的左连续区间 if(st[i<<1].ls==st[i<<1].r-st[i<<1].l+1) { st[i].ls+=st[(i<<1)|1].ls; } //如果右孩子都是连续区间,那么父节点的右连续区间除了右孩子的右连续区间,还要加上左孩子的右连续区间 if(st[(i<<1)|1].rs==st[(i<<1)|1].r-st[(i<<1)|1].l+1) { st[i].rs+=st[i<<1].rs; } } int query(int i,int p) { //该区间没有1或者找到该位置或者该区间全是1 if(st[i].ms==0||st[i].l==st[i].r||st[i].ms==st[i].r-st[i].l+1) return st[i].ms; int mid=(st[i].l+st[i].r)>>1; if(p<=mid)//向左孩子中查询 { if(p>=st[i<<1].r-st[i<<1].rs+1) //st[i<<1].r-st[i<<1].rs+1为左孩子节点右连续区间的左边界,比如[6,8]的长度为3,那么左边界就为8-3+1=6 return query(i<<1,p)+query((i<<1)|1,mid+1); else return query(i<<1,p); //如果位置p在左孩子节点的右连续区间中,那么还要加上右孩子节点的左连续区间 //比如左孩子[1,4],其右连续区间为[2,4],右孩子节点为[5,7],右孩子左连续区间为[5,6],位置p为3,那么包括3的连续区间不仅在[2,4]中, //还在[5,6]中,即包括3的最大连续区间为[2,6] } else//在右孩子中查询 { if(p<=st[(i<<1)|1].l+st[(i<<1)|1].ls-1) return query((i<<1)|1,p)+query(i<<1,mid); else return query((i<<1)|1,p); //如果位置p在右孩子节点的左连续区间中,还要加上左孩子节点的右连续区间 } } int n,m,p; char cm[2]; stack<int>sta; int main() { while(rd2(n,m)!=EOF) { while(!sta.empty()) sta.pop(); build(1,1,n); while(m--) { scanf("%s",cm); if(cm[0]=='D') { rd(p); update(1,p,'D'); sta.push(p); } else if(cm[0]=='R') { p=sta.top(); sta.pop(); update(1,p,'R'); } else { rd(p); printf("%d\n",query(1,p)); } } } return 0; }
hdu 3974 Assign the task
http://acm.hdu.edu.cn/showproblem.php?pid=3974
有n个员工,编号1-n,存在一些上司下属关系, a是c的上司,c是b的上司,那么a也是b的上司,没有上司的员工成为领导。给出n-1条上司下属关系,那么这n个员工关系就组成了一棵树,当给编号为i的员工分配任务时,该员工和其所有的下属都接受刚分配的任务,放弃前一个任务(如果有的话),查询编号为i的员工当前正做着哪一个任务。任务由数字表示。
在树中,父亲节点以及其所有的子节点是同时更新的,我们首先要把这些节点映射到区间上去,用dfs给这些点重新编号,start[i]表示该节点的新编号,end[i]表示其所有子节点的最后一个编号,那么执行更新操作时,只要对区间[start[i], end [i] ] 进行更新就可以了,把该区间的任务替换为新任务。
只要把树映射到线段区间上,就好操作了,建立线段树对其操作,区间更新,查询单点值,lazy表示分配的任务。
#define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=50010; int number;//给节点编号 int start[maxn],end[maxn];//第i个节点的编号,以及其孩子节点的最后一个编号,要更新,就更新这一段区间,对一个点操作,也就是对一段区间操作 vector<int>vc[maxn];//邻接表存边 void init() { number=0; for(int i=0;i<maxn;i++) vc[i].clear(); } void dfs(int u)//编号 { ++number; start[u]=number; int len=vc[u].size(); for(int i=0;i<len;i++) { dfs(vc[u][i]); } end[u]=number; } struct ST { int l,r; int lazy;//分配的是什么任务 }st[maxn<<2]; void pushDown(int i) { if(st[i].lazy!=-1) { st[i<<1].lazy=st[(i<<1)|1].lazy=st[i].lazy; st[i].lazy=-1; } } void build(int i,int l,int r) { st[i].l=l;st[i].r=r; st[i].lazy=-1; if(st[i].l==st[i].r) return; int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); } void update(int i,int l,int r,int val) { if(st[i].l==l&&st[i].r==r) { st[i].lazy=val; return; } pushDown(i); int mid=(st[i].l+st[i].r)>>1; if(r<=mid) update(i<<1,l,r,val); else if(l>mid) update((i<<1)|1,l,r,val); else { update(i<<1,l,mid,val); update((i<<1)|1,mid+1,r,val); } } int query(int i,int p) { if(st[i].l==st[i].r) return st[i].lazy; pushDown(i); int mid=(st[i].l+st[i].r)>>1; if(p<=mid) return query(i<<1,p); else return query((i<<1)|1,p); } int n,m; char cm[2]; int cas=1; int u,v; bool vis[maxn]; int main() { int t; rd(t); while(t--) { printf("Case #%d:\n",cas++); rd(n); init(); memset(vis,0,sizeof(vis)); for(int i=1;i<n;i++) { rd2(u,v); vc[v].push_back(u); vis[u]=1; } for(int i=1;i<=n;i++) { if(!vis[i])//找到根节点,只有根节点没有被访问过 { dfs(i); break; } } build(1,1,number); rd(m); while(m--) { scanf("%s",cm); if(cm[0]=='C') { rd(u); printf("%d\n",query(1,start[u]));//start[u]是该节点在线段中的编号 } else { rd2(u,v); update(1,start[u],end[u],v); } } } return 0; }
sdut 2880 Devour Magic
http://www.sdutacm.org/sdutoj/problem.php?action=showproblem&problemid=2880
线段树操作:区间更新,总区间同时增加一个数,查询指定区间的和,查询后将该区间清0.
用到了两个lazy,一个是增量,一个是是否清0,Pushdown的时候,清0的lazy优先。
#include <iostream> #include <stdio.h> #include <algorithm> #include <string.h> #include <stdlib.h> #include <cmath> #include <iomanip> #include <vector> #include <set> #include <map> #include <stack> #include <queue> #include <cctype> using namespace std; typedef long long ll; const int maxn=100010; struct ST { int l,r; ll sum; ll lazy;//懒惰标记 ll lazy0;//该区间是否清0了 }st[maxn<<2]; void pushUp(int i) { st[i].sum=st[i<<1].sum+st[(i<<1)|1].sum; } void pushDown(int i,int len) { if(st[i].lazy0!=0) { st[i<<1].lazy0=st[(i<<1)|1].lazy0=st[i].lazy0; st[i<<1].sum=0; st[(i<<1)|1].sum=0; st[i<<1].lazy=0; st[(i<<1)|1].lazy=0; st[i].lazy0=0; } if(st[i].lazy!=0) { st[i<<1].lazy+=st[i].lazy; st[(i<<1)|1].lazy+=st[i].lazy; st[i<<1].sum+=ll(len-(len>>1))*st[i].lazy; st[(i<<1)|1].sum+=ll(len>>1)*st[i].lazy; st[i].lazy=0; } } void build(int i,int l,int r) { st[i].l=l;st[i].r=r; st[i].lazy=st[i].lazy0=0; st[i].sum=0; if(st[i].l==st[i].r) return; int mid=(st[i].l+st[i].r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); } void update(int i,int l,int r,int val) { if(val!=0) { if(st[i].l==l&&st[i].r==r) { st[i].lazy+=val; st[i].sum+=ll(r-l+1)*val; return; } } else { if(st[i].l==l&&st[i].r==r) { st[i].sum=0; st[i].lazy0=1; st[i].lazy=0;//保证当前节点的维护的值正确,别忘了这一句 return; } pushDown(i,st[i].r-st[i].l+1); int mid=(st[i].l+st[i].r)>>1; if(r<=mid) update(i<<1,l,r,val); else if(l>mid) update((i<<1)|1,l,r,val); else { update(i<<1,l,mid,val); update((i<<1)|1,mid+1,r,val); } pushUp(i); } } ll query(int i,int l,int r) { if(st[i].l==l&&st[i].r==r) { return st[i].sum; } int mid=(st[i].l+st[i].r)>>1; pushDown(i,st[i].r-st[i].l+1); if(r<=mid) return query(i<<1,l,r); else if(l>mid) return query((i<<1)|1,l,r); else return query(i<<1,l,mid)+query((i<<1)|1,mid+1,r); } int t[maxn]; int n,q; ll ans; int main() { int cas; t[0]=0; scanf("%d",&cas); while(cas--) { ans=0; scanf("%d%d",&n,&q); build(1,1,n); for(int i=1;i<=q;i++) { int l,r; scanf("%d%d%d",&t[i],&l,&r); update(1,1,n,t[i]-t[i-1]); ans+=query(1,l,r); update(1,l,r,0); } //printf("%I64d\n",ans); cout<<ans<<endl; } return 0; }