zoukankan      html  css  js  c++  java
  • 二维差分

      二维差分和一维差分思路上并没有什么区别,具体实现的区别就在于一维的直接对区间两端差分就好了,而二维的多了一维需要处理。

      差分的思想是和前缀和有关的,一维的前缀和我们都懂求,那么二维的呢?

      如图

      因为是从左到右,从上到下的遍历,当要求红色部分,(0,0)到(i,j)处的前缀和时,我们黄色部分和蓝色部分已经是已知的了,而它们重叠的部分就是绿色部分,所以把黄色和蓝色部分的结果加起来,再减去绿色部分,最后加上(i,j)处的值就是(i,j)位置的前缀和了。

      所以,二维前缀和就是sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]

      而我们要求左上角是(x1,y1),右下角是(x2,y2)的矩形区间内的值处理出前缀和后也可以O(1)时间内求出来。

      如图

      我们要求紫色部分的值,我们已知的是黄色部分的值,但它多了两个蓝色部分的值,而两个蓝色部分有重叠了个绿色部分

      所以要求的区间内的值就是sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][x1-1]

    二哥种花生

      中文题,二维前缀和的练手题。

     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
    ",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("
    ");
    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
    ",area,cova);
    75             if(area==cova)
    76                 printf("YES
    ");
    77             else
    78                 printf("NO
    ");
    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
    ",ans,id);
    80     return 0;
    81 }
    差分前缀大综合

      

  • 相关阅读:
    WPS设置去广告
    Android,几款apk工具
    Eclipse 使用
    linux su和sudo命令的区别
    CentOS下安装SecureCRT的sz/rz工具包
    CentOS下安装SecureCRT的sz/rz工具包
    CentOS下安装SecureCRT的sz/rz工具包
    VMware虚拟机上网络连接(network type)的三种模式--bridged、host-only、NAT
    修改CentOS系统的默认启动级别
    修改CentOS系统的默认启动级别
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10753451.html
Copyright © 2011-2022 走看看