zoukankan      html  css  js  c++  java
  • 【BZOJ】4558: [JLoi2016]方

    【题意】给定有(n+1)*(m+1)个点的网格图,其中指定k个点不合法,求合法的正方形个数(四顶点合法)。

    【算法】计数

    【题解】斜着的正方形很麻烦,所以考虑每个斜正方形其外一定有正的外接正方形。

    也就是,一个边长为x的正放的正方形,同时代表x个正方形(其中1~x-1为斜正方形)

    num0:首先计算所有点合法时全图的正方形个数。

    枚举长度i,则num0=∑i*(n-i+1)*(m-i+1)。(长度为i的情况下,左上角多大的矩形能作为左上端点)

    num1:减去单个不合法点构成的正方形

    对于一个不合法点有四个方向,任意一个方向可以视为calc(l,r,h),l为向左延伸长度,r为向右,h为向上。

    包含斜放的正方形在内,也就是一条线段只要接触到该点就会有一个该点构成的正方形。

    考虑长度为1的情况是(0,1)(1,0),长度为2是(0,2)(1,1)(2,0),随长度递增答案有以下规律,只要判断h在哪个区间就可以快速计算答案:

    当长度为0,答案为0。

    当长度为1~min(l,r),数列递增,答案为(h+3)*h/2。

    当长度为min(l,r)+1~max(l,r),数列为常数min(l,r)+1,答案为(min+3)*min/2+(h-min)*(min+1)。(一边受限)

    当长度为max(l,r)+1~min(l,r)+max(l,r),数列递减,答案为(min+3)*min/2+(max-min)*(min+1)+(min*2+1-h+max)*(h-max)/2。(两边受限)

    当长度为min+max+1~+∞,答案为(min+3)*min/2+(max-min)*(min+1)+(min+1)*min/2

    细心推一推就可以了。

    num2:加上两个不合法点构成的正方形

    枚举两个不合法点(A,B)(C,D)的连边,有两种情况:

    作为边:另外两个点是(A+D-B,B+A-C)(C+D-B,D+A-C),或另一方向的(A+B-D,B+C-A)(C+B-D,D+C-A)。

    作为对角线,利用全等三角形解方程可得,另外两个点是((A-D+B+C)/2,B+C-(A-D+B+C)/2)((A-B+D+C)/2,A+D-(A-B+D+C)/2)。

    判断另外两个点是否整点和是否在界内。

    num3:减去三个不合法点构成的正方形

    在枚举计算num2的时候可以顺便求得(二分另外两个点判断是否是不合法点),当然这样每三个点会被计算3次,除以三即可。

    注意,每三个点构成了一个正方形时都应该统计一次,这样粗糙的统计才是容斥的基础。(手算一下就可以得知了)

    num4:加上四个不合法点构成的正方形

    计算num2时顺便求得,这样每四个点会被计算6次,除以6即可。

    综上所述,ans=num0-num1+num2-num3+num4。

    注意一些细节,如fabs(x-(int)(x+eps))<=eps。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<set>
    #include<vector>
    #include<map>
    #include<algorithm>
    #define ll long long
    #define lowbit(x) x&-x
    using namespace std;
    int read(){
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    int min(int a,int b){return a<b?a:b;}
    int max(int a,int b){return a<b?b:a;}
    int ab(int x){return x>0?x:-x;}
    //int MO(int x){return x>=MOD?x-MOD:x;}
    //void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
    /*------------------------------------------------------------*/
    const int inf=0x3f3f3f3f,maxn=1000010,MOD=1e8+7,maxk=2010;
    const double eps=1e-8;
    int n,m,kind,ans=0,two,three,four;
    struct cyc{
        int x,y;
        bool operator < (const cyc &a)const{
            return x<a.x||(x==a.x&&y<a.y);
        }
    }a[maxk],p1,p2;
    int MO(int x){return x>=MOD?x-MOD:x;}
    int calc(ll l,ll r,ll h){
        ll H,num=0,mins=min(l,r),maxs=max(l,r);
        if(h>mins)H=mins;else H=h;
        num+=(H+3)*H/2;
        if(h<=mins)return num%MOD;
        
        if(h>maxs)H=maxs;else H=h;
        num+=(H-mins)*(mins+1);
        if(h<=maxs)return num%MOD;
        
        if(h>mins+maxs)H=mins+maxs;else H=h;
        num+=(mins*2+1-H+maxs)*(H-maxs)/2;
        return num%MOD;
    }
    void check(){
        if(p1.x<0||p1.x>n||p2.x<0||p2.x>n||p1.y<0||p1.y>m||p2.y<0||p2.y>m)return;
        two++;
        int x=lower_bound(a+1,a+kind+1,p1)-a;
        bool o1=x>=1&&x<=kind&&(a[x].x==p1.x&&a[x].y==p1.y);
        x=lower_bound(a+1,a+kind+1,p2)-a;
        bool o2=x>=1&&x<=kind&&(a[x].x==p2.x&&a[x].y==p2.y);
        if(o1)three++;if(o2)three++;if(o1&&o2)four++;
    }
    bool T(double x){return fabs(x-(int)(x+eps))<=eps;}
    int main(){
        n=read();m=read();kind=read();
        for(int i=1;i<=min(n,m);i++){
            ans=MO(ans+1ll*(n-i+1)*(m-i+1)%MOD*i%MOD);
        }
        for(int i=1;i<=kind;i++){
            a[i].x=read();a[i].y=read();
            int num=0;
            num=MO(num+calc(a[i].x,n-a[i].x,a[i].y));
            num=MO(num+calc(a[i].x,n-a[i].x,m-a[i].y));
            num=MO(num+calc(a[i].y,m-a[i].y,a[i].x));
            num=MO(num+calc(a[i].y,m-a[i].y,n-a[i].x));
            num=MO(num+MOD-min(a[i].x,a[i].y)-min(a[i].x,m-a[i].y)-min(n-a[i].x,a[i].y)-min(n-a[i].x,m-a[i].y));
            ans=MO(ans+MOD-num);
        }
        sort(a+1,a+kind+1);
        two=0;three=0;four=0;
        for(int i=1;i<kind;i++){
            for(int j=i+1;j<=kind;j++){
                int A=a[i].x,B=a[i].y,C=a[j].x,D=a[j].y;
                p1=(cyc){A+D-B,B+A-C};p2=(cyc){C+D-B,D+A-C};
                check();
                p1=(cyc){A+B-D,B+C-A};p2=(cyc){C+B-D,D+C-A};
                check();
                if(T(1.0*(A+B+C-D)/2)&&T(1.0*(A-B+D+C)/2)){
                    p1=(cyc){(A-D+B+C)/2,B+C-(A-D+B+C)/2};p2=(cyc){(A-B+D+C)/2,A+D-(A-B+D+C)/2};
                    check();
                }
            }
        }
        ans=MO(ans+two);
        ans=MO(ans+MOD-three/3);
        ans=MO(ans+four/6);
        printf("%d",(ans%MOD+MOD)%MOD);
        return 0;
    }
    View Code
  • 相关阅读:
    LeetCode111 二叉树的最小深度
    LeetCode104 二叉树的最大深度
    LeetCode102 二叉树的层次遍历
    LeetCode94 二叉树的中序遍历
    LeetCode145 二叉树的后序遍历
    LeetCode144 二叉树的前序遍历
    计算机考研真题 最大序列和
    计算机考研真题 对称矩阵
    计算机考研真题 点菜问题
    计算机考研真题 数字反转
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7752926.html
Copyright © 2011-2022 走看看