一维差分

  比赛时遇见了个二维差分,很可惜没看出来,还想用线段树维护,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 } 
区间覆盖

猜你喜欢

转载自www.cnblogs.com/LMCC1108/p/10742972.html