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

 

关键      易错


 

【二维前缀和&差分】

前缀和:

sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + 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]

xy = b[i][j]

可得

x = (a - √(2b-a2))/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);                                  //
        ans=max(ans,num[n]+num[n-1]);
        printf("%lld\n",tot-ans);
    }
    return 0;
} 

 

 

 

上一篇:Java线程面试题 Top 50(转载)


下一篇:NFC简介