zoukankan      html  css  js  c++  java
  • [JLOI2016]方

    Description
    上帝说,不要圆,要方,于是便有了这道题。由于我们应该方,而且最好能够尽量方,所以上帝派我们来找正方形上帝把我们派到了一个有N行M列的方格图上,图上一共有(N+1)×(M+1)个格点,我们需要做的就是找出这些格点形成了多少个正方形(换句话说,正方形的四个顶点都是格点)。但是这个问题对于我们来说太难了,因为点数太多了,所以上帝删掉了这(N+1)×(M+1)中的K个点。既然点变少了,问题也就变简单了,那么这个时候这些格点组成了多少个正方形呢?

    Input
    第一行三个整数 N, M, K, 代表棋盘的行数、 列数和不能选取的顶点个数。 保证 N, M >= 1, K <=(N + 1) ×(M + 1)。约定每行的格点从上到下依次用整数 0 到 N 编号,每列的格点依次用 0到 M 编号。接下来 K 行,每行两个整数 x,y 代表第 x 行第 y 列的格点被删掉了。保证 0 <=x <=N<=10^6, 0 <=y<=M<=10^6,K<=2*1000且不会出现重复的格点。

    Output
    仅一行一个正整数, 代表正方形个数对 100000007( 10^8 + 7) 取模之后的值

    Sample Input
    2 2 4
    1 0
    1 2
    0 1
    2 1

    Sample Output
    1


    这种题肯定考虑容斥,那么答案即为所有正方形个数-包含一个删除点的正方形个数-包含两个点的……

    然后考虑一下所有正方形怎么求,主要是正方形可以斜着很烦啊……

    我们考虑一下对于如下的一个正方形,我们叫它((a,b))正方形好了……

    我们可以发现这个正方形占了((a+b)*(a+b))这么大一块空间,然后我们发现(a)的取值范围为([0,a+b]),所以第一部分答案我们就统计完了

    然后经过删除点的正方形个数呢?经过三个和四个的二分找一下即可,并不难;经过两个主要考虑对角线的情况,同样不难,所以现在最难的便是考虑经过一个点的情况

    对于一个点,和它相关的之后4个属性,(l,r,u,d)对吧

    然后我们考虑一下直的,也就是边平行于坐标轴的正方形

    可以发现个数即为(min(l,d)+min(d,r)+min(r,u)+min(u,l)),其他情况的正方形都是横跨了两个相邻象限的

    我们考虑形如这样的正方形,那么它显然也是一个((a,b))正方形,我们令(c=a+b),然后我们考虑一下(a)的取值范围

    易得(egin{cases}aleqslant r\a<c\a>0\ageqslant c-l(bleqslant l)end{cases})

    所以我们可以列出一个这样的柿子来计算

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

    但是这样显然不够优秀,因此我们可以人工分类讨论一下……

    于是我们就枚举两个断点,也就是(c=r+1),和(c=l+1)两个断点,那么在这些断点之间显然都是一次函数,那么我们就可以用等差数列求出中间的值

    int calc(int l,int r,int d){
    	if (!l||!r||!d)	return 0;
    	int res=0,limit=min(d,l+r),cl=1;
    	int v[3]={l+1,r+1,limit};
    	sort(v,v+3);
    	for (int i=0;i<3;i++){
    		int cr=v[i];
    		if (cr>limit)	break;
    		if (cr<2||cl==cr)	continue;
    		cl++;
    		int vl=min(r,cl-1)-max(1,cl-l)+1;
    		int vr=min(r,cr-1)-max(1,cr-l)+1;
    		res=(res+1ll*(vl+vr)*(cr-cl+1)/2)%Mod;
    		cl=cr;
    	}
    	return res;
    }
    

    然后就是考虑一些容斥,这个我就在代码里做一下注释吧……

    /*problem from Wolfycz*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define inf 0x7f7f7f7f
    #define Fi first
    #define Se second
    #define MK make_pair
    using namespace std;
    typedef long long ll;
    typedef unsigned int ui;
    typedef unsigned long long ull;
    inline char gc(){
    	static char buf[1000000],*p1=buf,*p2=buf;
    	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int frd(){
    	int x=0,f=1; char ch=gc();
    	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
    	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<3)+(x<<1)+ch-'0';
    	return x*f;
    }
    inline int read(){
    	int x=0,f=1; char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<3)+(x<<1)+ch-'0';
    	return x*f;
    }
    inline void print(int x){
    	if (x<0)	putchar('-');
    	if (x>9)	print(x/10);
    	putchar(x%10+'0');
    }
    const int N=2e3,Mod=1e8+7;
    const double eps=1e-8;
    pair<int,int>A[N+10];
    int n,m,k,Ans;
    int calc(int l,int r,int d){
    	if (!l||!r||!d)	return 0;
    	int res=0,limit=min(d,l+r),cl=1;
    	int v[3]={l+1,r+1,limit};
    	sort(v,v+3);
    	for (int i=0;i<3;i++){
    		int cr=v[i];
    		if (cr>limit)	break;
    		if (cr<2||cl==cr)	continue;
    		cl++;
    		int vl=min(r,cl-1)-max(1,cl-l)+1;
    		int vr=min(r,cr-1)-max(1,cr-l)+1;
    		res=(res+1ll*(vl+vr)*(cr-cl+1)/2)%Mod;
    		cl=cr;
    	}
    	return res;
    }
    int work(int u,int d,int l,int r){return (calc(l,r,d)+calc(l,r,u)+calc(u,d,l)+calc(u,d,r)+min(l,d)+min(d,r)+min(r,u)+min(u,l))%Mod;}
    bool ck(double x,double y){
    	int tx=fabs(x-(int)(x+0.5))<=eps?(int)(x+0.5):-1;
    	int ty=fabs(y-(int)(y+0.5))<=eps?(int)(y+0.5):-1;
    	return tx>=0&&tx<n&&ty>=0&&ty<m;
    }
    bool ck(pair<int,int>tmp){return tmp.Fi>=0&&tmp.Fi<n&&tmp.Se>=0&&tmp.Se<m;}
    int main(){
    	n=read()+1,m=read()+1,k=read();
    	for (int i=1;i<=n&&i<=m;i++)	Ans=(Ans+1ll*(n-i)*(m-i)%Mod*i%Mod)%Mod;
    	for (int i=1;i<=k;i++){
    		int x=read(),y=read();
    		Ans=(Ans-work(x,n-x-1,y,m-y-1)+Mod)%Mod;
    		A[i]=MK(x,y);
            //这里每次减去会导致包含两个点的正方形被算了两次
    	}
    	sort(A+1,A+1+k);
    	int cnt3=0,cnt4=0;
    	for (int i=1;i<=k;i++){
    		for (int j=i+1;j<=k;j++){
    			double Mx=(A[i].Fi+A[j].Fi)/2.0,My=(A[i].Se+A[j].Se)/2.0;
    			double hx=A[i].Fi-Mx,hy=A[i].Se-My;
    			if (ck(Mx-hy,My+hx)&&ck(Mx+hy,My-hx))	Ans++;
                //这里仅仅是枚举作为对角线的情况,会使得包含两个点的正方形的情况没有算全,但是包含三个点和四个点的正方形分别被考虑了一次两次
    			for (int p=-1;p<=1;p+=2){
    				double tx=A[i].Fi-A[j].Fi,ty=A[i].Se-A[j].Se;
    				pair<int,int>T1=MK(A[i].Fi-ty*p,A[i].Se+tx*p);
    				pair<int,int>T2=MK(A[j].Fi-ty*p,A[j].Se+tx*p);
    				if (!ck(T1)||!ck(T2))	continue;
    				int cnt=0; Ans++;//这里的Ans++是指完全考虑了包含两个点的情况,使得其不会被多减
    				if (binary_search(A+1,A+1+k,T1))	cnt++;
    				if (binary_search(A+1,A+1+k,T2))	cnt++;
    				if (cnt==1)	cnt3++;
    				else	if (cnt==2)	cnt3++,cnt4++;
                    //这里给三个点和四个点做一下记录
    			}
    		}
    	}
    	Ans-=cnt3/2,Ans-=cnt4/4;
        //包含三个点的在考虑一个点时被减去了三次,在考虑两个点时被加回来了三次,现在再次减去一次
        //包含四个点的也类似
    	printf("%d
    ",(Ans%Mod+Mod)%Mod);
    	return 0;
    }
    
  • 相关阅读:
    [2012-06-18]awk利用关联数组合并记录
    vue 中axios 的基本配置和基本概念
    django 中自带的加密方法
    哈希密码的使用
    django 中的闪现
    django中数据库的配置及相关增删改查
    sqlalchemy 的设置及使用
    mvc 模式和mtc 模式的区别
    flask 面试题
    flask 中orm关系映射 sqlalchemy的查询
  • 原文地址:https://www.cnblogs.com/Wolfycz/p/10041179.html
Copyright © 2011-2022 走看看