P3271 [JLOI2016]方 容斥+数学

题意:

给定一个(n*m)的矩阵,从中删去(k)个顶点,求最后能形成多少个正方形

范围&性质:(1le n,mle 10^6,1le kle 2000),正方形可以是斜着的(边不一定与网格图上的边重合)

分析:

(下文所有图片均来源于其他julao博客)

暴力做法:

枚举,复杂度(O(nmk))直接去世

正解:

通过容斥简化运算,记(f(i))表示至少包含(i)个被删除的点的正方形数目,最终的答案就是(f(0)-f(1)+f(2)-f(3)+f(4)),那么问题转化成了如何求(f(i))

首先我们考虑对于一个被删除的点,它能形成的正方形有多少种,情况如下图所示

img

我们很容易观察出正方形可以分为两类,斜着的和正着的,但是斜着的正方形可以在大的正方形里被统计,RT

img

我们按照横纵坐标的差值,对斜着的正方形进行定义,如下图的正方形我们可以称其为((a,b))正方形

image

那么对于一个被删除的点,它的属性可以用四个值表示分别为((u,d,l,r))

image

我们先考虑((0,x))正方形,也就是横平竖直的正方形,分象限考虑,这样的正方形的数目是(min(l,d)+min(d,r)+min(u,r)+min(u,l))

接下来考虑斜着的正方形,我们依旧分象限考虑,先考虑((l,r,d))所在象限,正方形((a,b))

image

我们列出此时对(a)的限制$ale r,ale a+b-1,1le a,a+b-lle a $改为枚举a+b,转化成代码就是

    for(int c=2;c<=d&&c<=l+r;c++)
    {
        ans+=min(r,c-1)-max(1,c-l)+1;
    }

但是,单次(O(n^2))的复杂度不优秀,其实(c)影响答案的值只在([l+1,r+1])范围内,因为小于(l+1)(min)会取1,大于$ r+1(时)min$会取r,在取值范围内是一次函数,所以可以对枚举进行优化,只枚举分界点

    int lim=min(l+r,h),res=0;
    int pos[3]={l+1,r+1,lim};
    sort(pos,pos+3);
	int cl=1,cr,vl,vr;
	for(int i=0;i<3;i++)
	{
		cr=pos[i];
		if(cr>lim) break;
		if(cr<2||cl==cr) continue;
		cl++;
		vl=min(r,cl-1)-max(cl-l,1)+1;
		vr=min(r,cr-1)-max(cr-l,1)+1;
		res=(res+(vr+vl)*(cr-cl+1)/2)%mod;
		cl=cr;
	}
	return res;

对于含两个以上的情况,枚举两个点,利用向量或者坐标等数学知识推出其他点的坐标

tip:对于含至少三个点的情况会重复计算(C_3^2)次,对于至少四个点的情况会重复计算(C_4^2)次,统计答案的时候直接除掉就可以

代码:

#include<bits/stdc++.h>
#define mk(x,y) make_pair(x,y)
using namespace std;

namespace zzc
{
	const int mod = 1e8+7;
	int n,m,k;
	set<pair<int,int> > p;
	
	bool check(int x,int y)
	{
		return x>=0&&x<=n&&y>=0&&y<=m;
	}
	
	int calc(int l,int r,int h)
	{
		int lim=min(l+r,h),res=0;
		int pos[3]={l+1,r+1,lim};
		sort(pos,pos+3);
		int cl=1,cr,vl,vr;
		for(int i=0;i<3;i++)
		{
			cr=pos[i];
			if(cr>lim) break;
			if(cr<2||cl==cr) continue;
			cl++;
			vl=min(r,cl-1)-max(cl-l,1)+1;
			vr=min(r,cr-1)-max(cr-l,1)+1;
			res=(res+(vr+vl)*(cr-cl+1)/2)%mod;
			cl=cr;
		}
		return res;
	}
	
	int calc0()
	{
		int res=0;
		for(int i=1;i<=min(n,m);i++)
		{
			res=(res+(long long)(m-i+1)*(n-i+1)%mod*i%mod)%mod;
		}
		return res;
	}
	
	int calc1()
	{
		int res=0;
		for(set<pair<int,int> >::iterator it=p.begin();it!=p.end();it++)
		{
			int x=it->first,y=it->second;
			res+=calc(x,n-x,y);
			res+=calc(x,n-x,m-y);
			res+=calc(m-y,y,x);
			res+=calc(m-y,y,n-x);
			res+=min(x,y);
			res+=min(x,m-y);
			res+=min(n-x,y);
			res+=min(n-x,m-y);
			res=(res+mod)%mod;
		}
		return res;
	}
	
	int calc2()
	{
		int res=0;
		for(set<pair<int,int> >::iterator it=p.begin();it!=p.end();it++)
		{
			for(set<pair<int,int> >::iterator jt=it;jt!=p.end();jt++)
			{
				if(it==jt) continue;
				int x1=it->first,y1=it->second,x2=jt->first,y2=jt->second;
				if(check(x1+y1-y2,y1-x1+x2)&&check(x2+y1-y2,y2-x1+x2)) res++;
				if(check(x1-y1+y2,y1+x1-x2)&&check(x2-y1+y2,y2+x1-x2)) res++;
				if((x1+x2+y1+y2)&1) continue;
				if(check((x1+x2+y1-y2)/2,(y1+y2-x2+x1)/2)&&check((x1+x2-y1+y2)/2,(y1+y2+x2-x1)/2)) res++;
			}
		}
		return res;
	}
	
	int calc3()
	{
		int res=0;
		for(set<pair<int,int> >::iterator it=p.begin();it!=p.end();it++)
		{
			for(set<pair<int,int> >::iterator jt=it;jt!=p.end();jt++)
			{
				if(it==jt) continue;
				int x1=it->first,y1=it->second,x2=jt->first,y2=jt->second;
				if(check(x1+y1-y2,y1-x1+x2)&&check(x2-y2+y1,y2+x2-x1))
				{
					if(p.count(mk(x1+y1-y2,y1-x1+x2))) ++res;
					if(p.count(mk(x2-y2+y1,y2+x2-x1))) ++res;
				}
				if(check(x1-y1+y2,y1+x1-x2)&&check(x2+y2-y1,y2-x2+x1))
				{
					if(p.count(mk(x1-y1+y2,y1+x1-x2))) ++res;
					if(p.count(mk(x2+y2-y1,y2-x2+x1))) ++res;
				}
				if(((y1+y2+x1+x2)&1)==0)
				{
					if(check((x1+x2+y2-y1)/2,(y1+y2-x2+x1)/2)&&check((x1+x2-y2+y1)/2,(y1+y2+x2-x1)/2))
					{
						if(p.count(mk((x1+x2+y2-y1)/2,(y1+y2-x2+x1)/2))) ++res;
						if(p.count(mk((x1+x2-y2+y1)/2,(y1+y2+x2-x1)/2))) ++res;
					}	
		    	}
	    	}
    	}
    	return res/3;
    }
    
    int calc4()
	{
		int res=0;
		for(set<pair<int,int> >::iterator it=p.begin();it!=p.end();++it)
		{
			for(set<pair<int,int> >::iterator jt=it;jt!=p.end();++jt)
			{
				if(it==jt)continue;
				int x1=it->first,y1=it->second,x2=jt->first,y2=jt->second;
				if(check(x1+y1-y2,y1-x1+x2)&&check(x2-y2+y1,y2+x2-x1))
				{
					if(p.count(mk(x1+y1-y2,y1-x1+x2)) && p.count(mk(x2-y2+y1,y2+x2-x1))) ++res;
				}
				if(check(x1-y1+y2,y1+x1-x2)&&check(x2+y2-y1,y2-x2+x1))
				{
					if(p.count(mk(x1-y1+y2,y1+x1-x2)) && p.count(mk(x2+y2-y1,y2-x2+x1))) ++res;
				}
				if(((y1+y2+x1+x2)&1)==0)
				{
					if(check((x1+x2+y2-y1)/2,(y1+y2-x2+x1)/2 && check((x1+x2-y2+y1)/2,(y1+y2+x2-x1)/2)))
					{
						if(p.count(mk((x1+x2+y2-y1)/2,(y1+y2-x2+x1)/2))&& p.count(mk((x1+x2-y2+y1)/2,(y1+y2+x2-x1)/2))) ++res;
					}	
				}
			}
		}
		return res/6;
    }
	
	
    void work()
	{
		int a,b;
		scanf("%d%d%d",&n,&m,&k);
		for(int i=1;i<=k;i++)
		{
			scanf("%d%d",&a,&b);
			p.insert(mk(a,b));
		}
		printf("%d
",(calc0()-calc1()+calc2()-calc3()+calc4()+mod)%mod);
	}
}

signed main()
{
	zzc::work();
	return 0;
 } 
原文地址:https://www.cnblogs.com/youth518/p/13734005.html