【二维差分】2018-2019ICPC焦作J - Carpets Removal

 

关键      易错


 

【二维前缀和&差分】

前缀和:

$$sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + {\color{Red} {a[i][j]}}$$

差分操作:

$a[x1][y1]$++$;$

$a[x1][y2+1]$--$;$

$a[x2+1][y1]$--$;$

$a[x2+1][y2+1]$++$;$

p.s. 可用同一个数组

 


 

【题目描述】

Problem - J - Codeforces

 

【题解】

利用二维差分记录每个格子被覆盖的次数 $cnt[i][j]$,覆盖该格子的地毯的编号$a[i][j]$ 和编号平方和 $b[i][j]$(其实更容易想到存储和 & 积,但是会爆)

对于 $cnt[i][j] == 1$ 的格子,$a[i][j]$ 即为覆盖该格子的地毯

对于 $cnt[i][j] == 2$ 的格子,假设覆盖该格子的两个地毯编号为 $x$ 和 $y$,则已知

$x + y = a[i][j]$

$x^{2}+y^{2}=b[i][j]$

可得

$x=\frac{a-\sqrt{2b-a^{2}}}{2}$

$y = a - x$

由此,可以统计出被且仅被某块地毯覆盖的格子数 $num[i]$;以及被且仅被某两块地毯覆盖的格子数 $mp[pair<ll,ll>(x,y)](x<=y)$

对于最终选取的两块地毯,有两种情况:相交或不相交

对于相交的情况:只有相交部分存在只被这两个地毯覆盖的格子时,撤走它们俩才会对结果有贡献。因此遍历格子,当遇到 $cnt[i][j] == 2$ 的格子时,统计撤掉覆盖该格子的地毯(编号 $x$,$y$)的结果

$num[x] + num[y] + mp[pair<ll,ll>(x,y)](x<=y)$

对于不相交的情况,只需要选出 $num[i]$ 中最大的两个相加。注意这里的逻辑,因为 $num[i]$ 统计的是被且仅被某块地毯覆盖的格子数,所以如果最大的两个其实相交了,他们重合且有效的部分也不会被算在内,因而会在相交的情况中被取代掉

最后取$max$

 

【代码】

涉及到的使用生疏的 map 操作:

 

map<pair<ll,ll>,ll> mp;
map<pair<ll,ll>,ll>::iterator it;

mp[pair<ll,ll>(x,y)]++;

for (it=mp.begin();it!=mp.end();it++){
    long long x=it->first.first,y=it->first.second;
    ans=max(ans,num[x]+num[y]+it->second);
}

 

完整代码:

 

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1505,N=3e5+5;
int T,n,m,cnt[M][M];
long long a[M][M],b[M][M],num[N],ans,tot;
int main()
{
    int x1,y1,x2,y2;
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&m);
        memset(cnt,0,sizeof(cnt));
        memset(a,0ll,sizeof(a));
        memset(b,0ll,sizeof(b));
        for (int i=1;i<=n;i++){
            scanf("%d%d%d%d",&x1,&x2,&y1,&y2);
            cnt[x1][y1]++;a[x1][y1]+=(ll)i;b[x1][y1]+=(ll)i*(ll)i;                   //
            cnt[x1][y2+1]--;a[x1][y2+1]-=(ll)i;b[x1][y2+1]-=(ll)i*(ll)i;
            cnt[x2+1][y1]--;a[x2+1][y1]-=(ll)i;b[x2+1][y1]-=(ll)i*(ll)i;
            cnt[x2+1][y2+1]++;a[x2+1][y2+1]+=(ll)i;b[x2+1][y2+1]+=(ll)i*(ll)i;
        }
        memset(num,0ll,sizeof(num));
        map<pair<ll,ll>,ll> mp;
        map<pair<ll,ll>,ll>::iterator it;
        tot=0ll;
        for (int i=1;i<=m;i++)
            for (int j=1;j<=m;j++){
                cnt[i][j]=cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1]+cnt[i][j];         //别忘了最后一项
                a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+a[i][j];                   //
                b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+b[i][j];                   //
                if (cnt[i][j]) tot++;
                if (cnt[i][j]==1) num[a[i][j]]++;
                else if (cnt[i][j]==2){
                    long long x=(a[i][j]-(ll)sqrt(2*b[i][j]-a[i][j]*a[i][j]))/2;
                    long long y=a[i][j]-x;
                    if (x>y) {ll t=x;x=y;y=t;}
                    mp[pair<ll,ll>(x,y)]++;
                }
            }
        ans=0ll;
        for (it=mp.begin();it!=mp.end();it++){
            long long x=it->first.first,y=it->first.second;
            ans=max(ans,num[x]+num[y]+it->second);            //
        }
        sort(num+1,num+1+n);                                  //先处理重合的情况,再排序,否则num数组就乱了
        ans=max(ans,num[n]+num[n-1]);
        printf("%lld\n",tot-ans);
    }
    return 0;
} 

 

 

 

 

 

 

 

posted @ 2022-01-29 23:21  Maaaaax  阅读(72)  评论(0)    收藏  举报