二維差分

  二維差分和一維差分思路上並無什麼區別,具體實現的區別就在於一維的直接對區間兩端差分就行了,而二維的多了一維須要處理。php

  差分的思想是和前綴和有關的,一維的前綴和咱們都懂求,那麼二維的呢?數組

  如圖ide

  由於是從左到右,從上到下的遍歷,當要求紅色部分,(0,0)到(i,j)處的前綴和時,咱們黃色部分和藍色部分已是已知的了,而它們重疊的部分就是綠色部分,因此把黃色和藍色部分的結果加起來,再減去綠色部分,最後加上(i,j)處的值就是(i,j)位置的前綴和了。spa

  因此,二維前綴和就是sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]code

  而咱們要求左上角是(x1,y1),右下角是(x2,y2)的矩形區間內的值處理出前綴和後也能夠O(1)時間內求出來。blog

  如圖get

  咱們要求紫色部分的值,咱們已知的是黃色部分的值,但它多了兩個藍色部分的值,而兩個藍色部分有重疊了個綠色部分it

  因此要求的區間內的值就是sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][x2-1]io

二哥種花生event

  中文題,二維前綴和的練手題。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long ll;
 5 ll sum[1018][1018]={0};
 6 int main()
 7 {
 8     int n,m,l,w;
 9     while(~scanf("%d%d",&n,&m))
10     {
11         for(int i=1;i<=n;i++)
12             for(int j=1;j<=m;j++)
13                 scanf("%lld",&sum[i][j]);
14         for(int i=1;i<=n;i++)
15             for(int j=1;j<=m;j++)
16                 sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
17         scanf("%d%d",&l,&w);
18         ll ans=0;
19         //題目給的l,w不是說(0,0)到(l,w)的矩形
20         //而是長爲l,寬爲w的矩形 
21         //枚舉左上角 
22         for(int x1=1;x1<=n;x1++)
23             for(int y1=1;y1<=m;y1++)
24             {
25                 int x2=x1+l-1,y2=y1+w-1;
26                 if(x2<=n&&y2<=m)//右下角在範圍內 
27                     ans=max(ans,sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]);
28             }
29         printf("%lld\n",ans);
30     }
31     return 0;
32 } 
一維都會了,二維也不難

  照這樣畫圖的方法咱們,也能夠得出差分值的放置位置,一維時是在須要更新的位置開始,不須要的位置結束,而二維的也同樣。

  若是咱們要在左上角是(x1,y1),右下角是(x2,y2)的矩形區間每一個值都+a

  如圖

  在咱們要的區間開始位置(x1,y1)處+a,根據前綴和的性質,那麼它影響的就是整個黃色部分,多影響了兩個藍色部分,因此在兩個藍色部分-a消除+a的影響,而兩個藍色部分重疊的綠色部分多受了個-a的影響,因此綠色部分+a消除影響。

  因此就是dif[x1][y1]+=a,dif[x1][y2+1]-=a,dif[x2+1][y1]-=a,dif[x2+1][y2+1]+=a

HDUOJ6514Monitor

  這道讓我開始學差分的題。。。

  題目大意:先給出一個n*m的範圍,而後給出p個監控矩形的左下角和右上角,最後有q個詢問,問給出左下角和右上角的矩形是否徹底被監控到。

  沒學差分以前一直想用線段樹來維護來作,但很麻煩根本不會作,而學了差分以後,由於p個矩形是已經選給出了,因此咱們能夠離線處理這些矩形,相似於一維的那道牛客的區間覆蓋的問題,咱們dif的差分數組能夠得出(i,j)這個位置被多少個監控監控到,而後咱們讓被監控到的點權值爲1,而後維護一個被監控到的點數的前綴和,那麼詢問的矩形要是被監控到的點等於面積,那麼就是yes,不然就是no,噁心的地方就在於這題不能開二維數組,須要降維處理。

 1 #include<cstdio>
 2 const int N=13142118;
 3 int dif[N],cov[N];
 4 //cov被監控到的點的前綴和 
 5 int main()
 6 {
 7     int n,m,p,q,x1,y1,x2,y2;
 8     while(~scanf("%d%d",&n,&m))
 9     {
10         for(int i=0;i<n;i++)
11             for(int j=0;j<m;j++)
12             {
13                 dif[i*m+j]=0;
14                 cov[i*m+j]=0;
15             }
16         scanf("%d",&p);
17         while(p--)
18         {
19             scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
20             x1--,y1--,x2--,y2--;
21             dif[x1*m+y1]++;
22             if(x2+1<n)
23                 dif[(x2+1)*m+y1]--;
24             if(y2+1<m)
25                 dif[x1*m+y2+1]--;
26             if(x2+1<n&&y2+1<m)
27                 dif[(x2+1)*m+(y2+1)]++;
28         }
29         for(int i=0;i<n;i++)
30         {
31             for(int j=0;j<m;j++)
32             {
33                 if(i)
34                     dif[i*m+j]+=dif[(i-1)*m+j];
35                 if(j)
36                     dif[i*m+j]+=dif[i*m+j-1];
37                 if(i&&j)
38                     dif[i*m+j]-=dif[(i-1)*m+j-1];
39                 //若是這個位置有被監控到就是設爲1 
40                 if(dif[i*m+j]>=1)
41                     cov[i*m+j]=1;
42                 else
43                     cov[i*m+j]=0;
44                 if(i)
45                     cov[i*m+j]+=cov[(i-1)*m+j];
46                 if(j)
47                     cov[i*m+j]+=cov[i*m+j-1];
48                 if(i&&j)
49                     cov[i*m+j]-=cov[(i-1)*m+j-1];
50             }
51         }
52 //        for(int i=n-1;i>=0;i--)
53 //        {
54 //            for(int j=0;j<m;j++)
55 //                if(dif[i*m+j]>=1)
56 //                    printf("1 ");
57 //                else
58 //                    printf("0 ");
59 //            printf("\n");
60 //        }
61         scanf("%d",&q);
62         while(q--)
63         {
64             scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
65             int area=(x2-x1+1)*(y2-y1+1),cova=0;
66             x1--,y1--,x2--,y2--;
67             cova+=cov[x2*m+y2];
68             if(x1-1>=0)
69                 cova-=cov[(x1-1)*m+y2];
70             if(y1-1>=0)
71                 cova-=cov[x2*m+y1-1];
72             if((x1-1)>=0&&(y1-1)>=0)
73                 cova+=cov[(x1-1)*m+(y1-1)];
74         //    printf("%d %d\n",area,cova);
75             if(area==cova)
76                 printf("YES\n");
77             else
78                 printf("NO\n");
79         }
80     }
81     return 0;
82 } 
差分太猛了

NOIAC#71畫畫鬼才

  中文題。

  副本的數量不少,咱們確定是不能夠直接暴力地去求一個副本跟其餘k-1張副本的差別值。因此咱們能夠藉助原圖,一個副本跟原圖相同的地方與其餘k-1張副本的圖的差別值是相同的,不一樣的就在於它修改的區域,那咱們能夠先求出原圖和全部副本的差別值之和,而後去掉要修改區域的差別值之和,而後補上修改以後的差別值以後。那怎麼快速地得出這些消息呢?確定也是不能暴力去求原圖和全部副本的差別值之和的,咱們能夠計算出col[i][j][k]即全部副本在(i,j)這個位置,顏色爲k的個數(由於顏色最多26種,能夠快速求解),這樣val[i][j]原圖與全部副本在(i,j)的差別值就是for(int k=0;k<s;k++) val[i][j]+=abs(a[i][j]-k)*col[i][j][k]其中a[i][j]爲原圖(i,j)位置的顏色。而後咱們再對col和val求一個前綴和,就能夠快速得出所要區域的信息。

 1 #include<cstdio>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const int N=1108,K=301108;
 7 char mp[N],cc[3];
 8 ll val[N][N],dif[N][N][28],col[N][N][28]; 
 9 int x1[K],y1[K],x2[K],y2[K],c[K],a[N][N];
10 ll getsum(int a,int b,int c,int d,int op)
11 {
12     if(op==-1)
13         return val[c][d]-val[c][b-1]-val[a-1][d]+val[a-1][b-1];
14     return col[c][d][op]-col[c][b-1][op]-col[a-1][d][op]+col[a-1][b-1][op];
15 }
16 int main()
17 {
18     int n,m,f,s;
19     scanf("%d%d%d%d",&n,&m,&f,&s);
20     for(int i=1;i<=n;i++)
21     {
22         scanf("%s",mp+1);
23         for(int j=1;j<=m;j++)
24             a[i][j]=mp[j]-'a';
25     }
26     for(int i=0;i<f;i++)
27     {
28         scanf("%d%d%d%d%s",&x1[i],&y1[i],&x2[i],&y2[i],cc);
29         c[i]=cc[0]-'a';
30         //差分處理副本的塗色 
31         dif[x1[i]][y1[i]][c[i]]++;
32         dif[x2[i]+1][y1[i]][c[i]]--;
33         dif[x1[i]][y2[i]+1][c[i]]--;
34         dif[x2[i]+1][y2[i]+1][c[i]]++;
35     }
36     for(int i=1;i<=n;i++)
37         for(int j=1;j<=m;j++)
38         {
39             ll difs=0;
40             //difs記錄(i,j)位置有多少個副本進行了塗色 
41             for(int k=0;k<s;k++)
42             {
43                 dif[i][j][k]+=dif[i-1][j][k]+dif[i][j-1][k]-dif[i-1][j-1][k];
44                 col[i][j][k]=dif[i][j][k];
45                 difs+=dif[i][j][k];
46             }
47             col[i][j][a[i][j]]+=f-difs;
48             //沒進行塗色的就和原圖顏色同樣 
49         }
50     ll sum=0;
51     for(int i=1;i<=n;i++)
52         for(int j=1;j<=m;j++)
53         {
54             for(int k=0;k<s;k++)
55                 val[i][j]+=abs(a[i][j]-k)*col[i][j][k];
56             sum+=val[i][j];
57             //sum就記錄原圖與全部副本總的差別值之和 
58         }
59     //維護col和val的前綴和之和 
60     for(int i=1;i<=n;i++)
61         for(int j=1;j<=m;j++)
62         {
63             for(int k=0;k<s;k++)
64                 col[i][j][k]+=col[i-1][j][k]+col[i][j-1][k]-col[i-1][j-1][k];
65             val[i][j]+=val[i-1][j]+val[i][j-1]-val[i-1][j-1];
66         }
67     int id; 
68     ll ans=1e18;
69     for(int i=0;i<f;i++)
70     {
71         //先將修改區域原來的差別值之和去掉 
72         ll dis=sum-getsum(x1[i],y1[i],x2[i],y2[i],-1);
73         //再根據新的顏色計算這個區域新的差別值之和 
74         for(int j=0;j<s;j++)
75             dis+=abs(c[i]-j)*getsum(x1[i],y1[i],x2[i],y2[i],j);
76         if(dis<ans)
77             ans=dis,id=i+1;
78     }
79     printf("%lld %d\n",ans,id);
80     return 0;
81 }
差分前綴大綜合
相關文章
相關標籤/搜索