比赛时遇见了个二维差分,很可惜没看出来,还想用线段树维护,wtcl。痛心之余,回来补一补差分。
差分一般用来解决区间操作,而区间操作当然可以用线段树写,但相比较下,差分更好实现而且更快,不过差分只能处理离线问题,不能一边更新一边查询。
差分的思想很简单,我们要在一个区间[l,r]内都加上一个数a,那么我们可以让sum[l]处+a,sum[r]处-a,那么用一个数dif记录sum区间的前缀和,这样dif在l处加上a,在r处减去a,这样在i处的dif就是这个位置变化的值,也就是实现了区间修改,如果不明白的话可以通过下面的题理解。
Color the ball HDU - 1556
题目大意:n个点,n次操作,每次对一个区间涂色,最后问每个点被涂了多少次。
1 #include<cstdio> 2 const int N=100118; 3 int sum[N],l,r; 4 int main() 5 { 6 int n; 7 while(scanf("%d",&n)&&n) 8 { 9 for(int i=1;i<=n;i++) 10 sum[i]=0; 11 for(int i=1;i<=n;i++) 12 { 13 scanf("%d%d",&l,&r); 14 sum[l]++; 15 sum[r+1]--; 16 } 17 int dif=0; 18 for(int i=1;i<=n;i++) 19 { 20 if(i>1) 21 printf(" "); 22 dif+=sum[i]; 23 printf("%d",dif); 24 } 25 printf("\n"); 26 } 27 return 0; 28 }
Tallest Cow POJ - 3263
题目大意:n头牛,第i处的牛最高为h有r组关系,每组一个a,b意味着a能看到b,即a和b中间的都严格低于a和b,问每个牛最高的可能高度。
我们假设每个牛一开始都是最高的高度h,那么每组a,b关系其实就是[a+1,b-1]处的牛高度都减1。
1 #include<cstdio> 2 #include<algorithm> 3 #include<map> 4 using namespace std; 5 const int N=11108; 6 int high[N]; 7 map<int,int> m; 8 inline void init(int n) 9 { 10 m.clear(); 11 for(int i=1;i<=n;i++) 12 high[i]=0; 13 } 14 int main() 15 { 16 int n,pos,maxh,r,a,b; 17 while(~scanf("%d%d%d%d",&n,&pos,&maxh,&r)) 18 { 19 init(n); 20 while(r--) 21 { 22 scanf("%d%d",&a,&b); 23 if(a>b) 24 swap(a,b); 25 if(b-a<=1||m[a*n+b])//map去重边 26 continue; 27 m[a*n+b]=1; 28 high[a+1]--; 29 high[b]++; 30 } 31 int dif=0;//dif记录差分值 32 for(int i=1;i<=n;i++) 33 { 34 dif+=high[i]; 35 printf("%d\n",dif+maxh); 36 } 37 } 38 return 0; 39 }
牛客练习赛34little w and Segment Coverage
题目大意:有n个点,有m个区间,问如果去掉其中一个区间没被任意区间覆盖的点最少有多少个,已经相应的区间编号,多个符合要求的区间输出最大编号的。
先差分处理得出每个点被多少个区间覆盖,然后记录没被覆盖的点,以及维护只被覆盖一次的点的数目的前缀和,那么去掉某个区间后,没被覆盖的点就sum[r]-sum[l-1]+原先就没被覆盖的点
1 #include<cstdio> 2 const int N=1000118; 3 int sum[N]={0},l[N],r[N]; 4 int main() 5 { 6 int n,m; 7 scanf("%d%d",&n,&m); 8 for(int i=1;i<=m;i++) 9 { 10 scanf("%d%d",&l[i],&r[i]); 11 sum[l[i]]++; 12 sum[r[i]+1]--; 13 } 14 int dif=0,no=0;//dif记录覆盖的区间数,no记录没被区间覆盖的点 15 for(int i=1;i<=n;i++) 16 { 17 dif+=sum[i]; 18 sum[i]=dif; 19 if(sum[i]==0) 20 no++; 21 } 22 //此时的sum[i]就表示i这个点被多少个区间覆盖了 23 for(int i=1;i<=n;i++) 24 { 25 if(sum[i]!=1) 26 sum[i]=0; 27 //因为题目要求去掉一个区间后,剩余没被覆盖的点, 28 //所以只统计被一个区间覆盖的点 29 sum[i]+=sum[i-1]; 30 } 31 //此时的sum[i]表示[1,i]区间内只被一个区间覆盖的点有多少个 32 int id=1,len=n+1; 33 for(int i=1;i<=m;i++) 34 { 35 int dis=sum[r[i]]-sum[l[i]-1]; 36 if(dis<=len) 37 id=i,len=dis; 38 } 39 printf("%d %d\n",id,len+no); 40 return 0; 41 }