Count the Colors
Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu
Description
Your task is counting the segments of different colors you can see at last.
Input
Each of the following n lines consists of exactly 3 nonnegative integers separated by single spaces:
x1 x2 c
x1 and x2 indicate the left endpoint and right endpoint of the segment, c indicates the color of the segment.
All the numbers are in the range [0, 8000], and they are all integers.
Input may contain several data set, process to the end of file.
Output
If some color can't be seen, you shouldn't print it.
Print a blank line after every dataset.
Sample Input
Sample Output
1 1
0 2
1 1
线段树区间更新的变形
题意:
在一条长度为8000的线段上染色,每次把区间[a,b]染成c颜色。显然,后面染上去的颜色会覆盖掉之前的颜色。
求染完之后,每个颜色在线段上有多少个间断的区间。
用区间更新的方式,对于区间内的先不更新,当出现新的线段覆盖的时候在pushdown,mark[]遍历一下离根最近的color不为-1(为染色)的就行了,最后通过sum统计出从到大的就好了。
PS:开始的时候由于用来之前的部分代码,结果在pushdown的时候错误成了
tree[tmp].color += tree[x].color;结果出了个Segmentation Fault 从来没有见过的错,结果是ZOJ越界(RE)的错误,查了好久。。。
//线段树区间更新 #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<vector> #include<queue> #include<stack> #include<iomanip> #include<string> #include<climits> #include<cmath> #define INF 0x3f3f3f3f #define MAX 8010 #define LL long long using namespace std; struct Tree { int l,r; int color; }; Tree tree[MAX*4]; void pushdown(LL x) ///用于更新color数组 { LL tmp = x<<1 ; tree[tmp].color = tree[x].color; ///由子节点通过增加 tree[tmp+1].color = tree[x].color; tree[x].color=-1; } void build(int l,int r,int x) { tree[x].l=l , tree[x].r=r , tree[x].color=-1; if(l==r) return ; int tmp=x<<1; int mid=(l+r)>>1; build(l,mid,tmp); build(mid+1,r,tmp+1); } void update(int l,int r,int c,int x) ///分别表示区间的左 , 右 , 增加的值 ,当前父亲节点 { if(r<tree[x].l||l>tree[x].r) return ; if(l<=tree[x].l&&r>=tree[x].r) ///该区间为需要更新区间的子区间 { tree[x].color = c; return ; } if(tree[x].color!=-1) pushdown(x); ///更新从上向下更新color update(l,r,c,x<<1); update(l,r,c,(x<<1)+1); } int mark[MAX<<2],coun=0; ///注意大小 void query(int l ,int r ,int x ) { if((l==tree[x].l&&r==tree[x].r && tree[x].color!=-1) || tree[x].l == tree[x].r){ mark[coun++] = tree[x].color; ///要计算的区间包括了该区间 return ; } LL tmp=x<<1; LL mid=(tree[x].l+tree[x].r)>>1; if(r<=mid) return query(l,r,tmp); else if(l>mid) return query(l,r,tmp+1); else{ query(l,mid,tmp) ; query(mid+1,r,tmp+1); } } int main() { int x1[MAX<<2],x2[MAX<<2],c[MAX<<2]; int sum[MAX<<2]; int mmax=-1,m; while(~scanf("%d",&m)) { coun = 0; mmax=-1; memset(mark,-1,sizeof(mark)); memset(sum,0,sizeof(sum)); for(int i=0;i<m;i++) { scanf("%d%d%d",&x1[i],&x2[i],&c[i]); if(mmax<x1[i]) mmax = x1[i]; if(mmax<x2[i]) mmax = x2[i]; } build(1,mmax,1); for( int i = 0;i<m; i++ ){ if(x1[i]+1>x2[i]) continue; update(x1[i]+1,x2[i],c[i],1); ///这个地方需要加1,减1因为线段树是从1开始的所以表示单位没有0,因此不好处理 } if(mmax<0) continue; query(1,mmax,1); for(int i=0;i<coun;){ ///注意统计的方式,下面的while不能少 if(mark[i]==-1){i++ ;continue;} int x = mark[i]; while(x == mark[++i] && i<coun); sum[x]++; } for(int i=0;i<8010;i++) if(sum[i]) printf("%d %d\n",i,sum[i]); printf("\n"); } return 0; }
因为在更新时候和Build不一样,所以代码风格很差,所以做一个整洁的。。。(小区别:见下面!!!)
但是之后再看好像下面的没有上面的更加简洁,因为上面的只要在函数开头加一个返回条件就行了,但是下面的需要增加好几个if。。。
#pragma comment(linker, "/STACK:102400000,102400000" #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define MAX 8010 #define INF 0x3f3f3f3f #define LL long long #define pii pair<int,int> #define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) ///map<int,int>mmap; ///map<int,int >::iterator it; using namespace std; struct Tree { int l,r; int color; }; Tree tree[MAX*4]; void pushdown(LL x) ///用于更新color数组 { LL tmp = x<<1 ; tree[tmp].color = tree[x].color; ///由子节点通过增加 tree[tmp+1].color = tree[x].color; tree[x].color=-1; } void build(int l,int r,int x) { tree[x].l=l , tree[x].r=r , tree[x].color=-1; if(l==r) return ; int tmp=x<<1; int mid=(l+r)>>1; build(l,mid,tmp); build(mid+1,r,tmp+1); } void update(int l,int r,int c,int x) ///分别表示区间的左 , 右 , 增加的值 ,当前父亲节点 { if(r<tree[x].l||l>tree[x].r) return ; ///!!!其实相较于上面一个代码,其实这里没必要返回了,因为已经在下面更改的if else else中判断了,但是最好还是这么写 if(l<=tree[x].l&&r>=tree[x].r) ///该区间为需要更新区间的子区间 { tree[x].color = c; return ; } if(tree[x].color!=-1) pushdown(x); ///更新从上向下更新color LL tmp=x<<1; LL mid=(tree[x].l+tree[x].r)>>1; if(r<=mid) update(l,r,c,tmp); else if(l>mid) update(l,r,c,tmp+1); else{ update(l,mid,c,tmp) ; update(mid+1,r,c,tmp+1); } } int mark[MAX<<2],coun=0; ///注意大小 void query(int l ,int r ,int x ) { if((l==tree[x].l&&r==tree[x].r && tree[x].color!=-1) || tree[x].l == tree[x].r){ mark[coun++] = tree[x].color; ///要计算的区间包括了该区间 return ; } LL tmp=x<<1; LL mid=(tree[x].l+tree[x].r)>>1; if(r<=mid) query(l,r,tmp); else if(l>mid) query(l,r,tmp+1); else{ query(l,mid,tmp) ; query(mid+1,r,tmp+1); } } int main() { int x1[MAX<<2],x2[MAX<<2],c[MAX<<2]; int sum[MAX<<2]; int mmax=-1,m; while(~scanf("%d",&m)) { coun = 0; mmax=-1; memset(mark,-1,sizeof(mark)); memset(sum,0,sizeof(sum)); for(int i=0;i<m;i++) { scanf("%d%d%d",&x1[i],&x2[i],&c[i]); if(mmax<x1[i]) mmax = x1[i]; if(mmax<x2[i]) mmax = x2[i]; } build(1,mmax,1); for( int i = 0;i<m; i++ ){ if(x1[i]+1>x2[i]) continue; update(x1[i]+1,x2[i],c[i],1); ///这个地方需要加1,减1也可以,如下 } if(mmax<0) continue; query(1,mmax,1); for(int i=0;i<coun;){ ///注意统计的方式,下面的while不能少 if(mark[i]==-1){i++ ;continue;} int x = mark[i]; while(x == mark[++i] && i<coun); sum[x]++; } for(int i=0;i<8010;i++) if(sum[i]) printf("%d %d\n",i,sum[i]); printf("\n"); } return 0; }
减1的,大同小异
//线段树区间更新 #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<vector> #include<queue> #include<stack> #include<iomanip> #include<string> #include<climits> #include<cmath> #define INF 0x3f3f3f3f #define MAX 8010 #define LL long long using namespace std; struct Tree { int l,r; int color; }; Tree tree[MAX*4]; void pushdown(LL x) ///用于更新color数组 { LL tmp = x<<1 ; tree[tmp].color = tree[x].color; ///由子节点通过增加 tree[tmp+1].color = tree[x].color; tree[x].color=-1; } void build(int l,int r,int x) { tree[x].l=l , tree[x].r=r , tree[x].color=-1; if(l==r) return ; int tmp=x<<1; int mid=(l+r)>>1; build(l,mid,tmp); build(mid+1,r,tmp+1); } void update(int l,int r,int c,int x) ///分别表示区间的左 , 右 , 增加的值 ,当前父亲节点 { if(r<tree[x].l||l>tree[x].r) return ; if(l<=tree[x].l&&r>=tree[x].r) ///该区间为需要更新区间的子区间 { tree[x].color = c; return ; } if(tree[x].color!=-1) pushdown(x); ///更新从上向下更新color update(l,r,c,x<<1); update(l,r,c,(x<<1)+1); } int mark[MAX<<2],coun=0; ///注意大小 void query(int l ,int r ,int x ) { if((l==tree[x].l&&r==tree[x].r && tree[x].color!=-1) || tree[x].l == tree[x].r){ mark[coun++] = tree[x].color; ///要计算的区间包括了该区间 return ; } LL tmp=x<<1; LL mid=(tree[x].l+tree[x].r)>>1; if(r<=mid) return query(l,r,tmp); else if(l>mid) return query(l,r,tmp+1); else{ query(l,mid,tmp) ; query(mid+1,r,tmp+1); } } int main() { int x1[MAX<<2],x2[MAX<<2],c[MAX<<2]; int sum[MAX<<2]; int mmax=-1,m; while(~scanf("%d",&m)) { coun = 0; mmax=-1; memset(mark,-1,sizeof(mark)); memset(sum,0,sizeof(sum)); for(int i=0;i<m;i++) { scanf("%d%d%d",&x1[i],&x2[i],&c[i]); if(mmax<x1[i]) mmax = x1[i]; if(mmax<x2[i]) mmax = x2[i]; } build(0,mmax-1,1); for( int i = 0;i<m; i++ ){ if(x1[i]>x2[i]-1) continue; update(x1[i],x2[i]-1,c[i],1); ///这个地方需要加1,减1因为线段树是从1开始的所以表示单位没有0,因此不好处理 } if(mmax<0) continue; query(0,mmax-1,1); for(int i=0;i<coun;){ ///注意统计的方式,下面的while不能少 if(mark[i]==-1){i++ ;continue;} int x = mark[i]; while(x == mark[++i] && i<coun); sum[x]++; } for(int i=0;i<8010;i++) if(sum[i]) printf("%d %d\n",i,sum[i]); printf("\n"); } return 0; }