关键 易错
【二维前缀和&差分】
前缀和:
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. 可用同一个数组
【题目描述】
【题解】
利用二维差分记录每个格子被覆盖的次数 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;
}