题意:
m*m的网格上覆盖了n个长方形
问拿走2个多少个长方形之后,最少有多少个格子没有被长方形覆盖
解决两个问题
1、如何知道每个格子被几个长方形覆盖
2、如何知道被1和2个长方形覆盖的格子是被哪个长方形覆盖(被>=3个长方覆盖的格子没有贡献)
如何知道每个格子被几个长方形覆盖?
二维差分前缀和即可,设差分前缀和数组为cnt[i][j]
如何知道被1个长方形覆盖的格子是被哪个长方形覆盖?
对每个格子,用sum[i][j]记录盖在这个格子上的长方形的编号之和
对于cnt[i][j]=1的格子(i,j),sum[i][j]即是覆盖它的长方形编号
如何知道被2个长方形覆盖的格子是被哪2个长方形覆盖?
即已知x+y=sum[i][j],再加什么信息可以唯一的确定一组x和y
可以记录编号的平方和
即pow2[i][j]表示记录盖在这个格子上的长方形的编号的平方和
x+y=sum
x*x+y*y=pow2
求x,y
∵(x+y)*(x+y)=x*x+y*y+2*x*y=sum*sum
∴2*x*y=sum*sum-pow2
∴(x-y)*(x-y)=x*x+y*y-2*x*y=2*pow2-sum*sum
∴x-y=sqrt(2*pow2-sum*sum)
∴x=(sum+sqrt(2*pow2-sum*sum) )/2
∴y=sum-x
如何去除长方形,能够新增最多的不被覆盖的格子?
我们有两种去除长方形的方案
方案1:选择两个只被一个长方形盖住的格子,去掉对应的长方形
利用sum数组,对每个cnt=1的格子统计一下,即可算出每个长方形覆盖的且只被这个长方形覆盖的格子数量
方案2:选择一个被两个长方形盖住的格子,去掉对应的长方形
对每个cnt=2的格子,算出它对应的两个长方形,然后排序,满足排完序后对应一样的长方形排在一起即可
假设是a 和 b
那就是只被a覆盖的长方形+只被b覆盖的长方形+对应是a和b的格子数量
答案就是所有的格子数量-初始没有被长方形覆盖的格子数量-上面两种方案的 最大值
#include<cmath> #include<cstdio> #include<algorithm> using namespace std; #define N 1502 #define M 300001 typedef long long LL; int cnt[N][N]; LL sum[N][N],pow2[N][N]; int s1[M]; struct node { int a,b; }s2[N*N]; bool cmp(node p,node q) { if(p.a!=q.a) return p.a<q.a; return p.b<q.b; } int main() { // freopen("data.txt","r",stdin); // freopen("my.txt","w",stdout); int T,n,m; int xl,xr,yu,yd; int s0,n3; int k1,k2; int ans,tmp,last,now; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<=m;++i) for(int j=0;j<=m;++j) cnt[i][j]=sum[i][j]=pow2[i][j]=0; for(int i=1;i<=n;++i) { scanf("%d%d%d%d",&yu,&yd,&xl,&xr); ++cnt[yu][xl]; --cnt[yu][xr+1]; --cnt[yd+1][xl]; ++cnt[yd+1][xr+1]; sum[yu][xl]+=i; sum[yu][xr+1]-=i; sum[yd+1][xl]-=i; sum[yd+1][xr+1]+=i; pow2[yu][xl]+=1ll*i*i; pow2[yu][xr+1]-=1ll*i*i; pow2[yd+1][xl]-=1ll*i*i; pow2[yd+1][xr+1]+=1ll*i*i; } // for(int i=1;i<=m;++i,printf(" ")) // for(int j=1;j<=m;++j) // printf("%d ",cnt[i][j]); for(int i=1;i<=m;++i) for(int j=1;j<=m;++j) { cnt[i][j]=cnt[i][j]+cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1]; sum[i][j]=sum[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]; pow2[i][j]=pow2[i][j]+pow2[i-1][j]+pow2[i][j-1]-pow2[i-1][j-1]; } // for(int i=1;i<=m;++i,printf(" ")) // for(int j=1;j<=m;++j) // printf("%d ",cnt[i][j]); s0=0; for(int i=1;i<=n;++i) s1[i]=0; n3=0; for(int i=1;i<=m;++i) for(int j=1;j<=m;++j) { if(!cnt[i][j]) ++s0; else if(cnt[i][j]==1) ++s1[sum[i][j]]; else if(cnt[i][j]==2) { ++n3; s2[n3].a=(sum[i][j]+sqrt(2*pow2[i][j]-sum[i][j]*sum[i][j]))/2; s2[n3].b=sum[i][j]-s2[n3].a; //if(s2[n3].a>s2[n3].b) swap(s2[n3].a,s2[n3].b); } } //printf("s0=%d ",s0); //for(int i=1;i<=n;++i) if(s1[i]) printf("s1[%d]=%d ",i,s1[i]); ans=m*m-s0; k1=k2=0; for(int i=1;i<=n;++i) if(s1[i]>=k1) { k2=k1; k1=s1[i]; } else if(s1[i]>k2) k2=s1[i]; tmp=k1+k2; sort(s2+1,s2+n3+1,cmp); last=0; now=1; while(now<=n3) { last=now; while(now<=n3 && s2[now].a==s2[last].a && s2[now].b==s2[last].b) ++now; tmp=max(tmp,s1[s2[last].a]+s1[s2[last].b]+now-last); // printf("s1[%d]=%d s2[%d]=%d two=%d ",s2[last].a,s1[s2[last].a],s2[last].b,s1[s2[last].b],now-last); } ans-=tmp; printf("%d ",ans); } }