BZOJ4558 [JLoi2016]方

KuribohG神犇说过,看到计数想容斥

尽管我在考场上也一直在默念这句话,但是并没有卵用,谁让我这么弱呢-_-

于是我们考虑考虑容斥,发现答案=随便选-sigma 以第i个坏点为顶点的正方形个数+sigma 同时以i,j为顶点的正方形个数-sigma 同时以i,j,k为顶点的正方形个数+sigma 同时以i,j,k,l为顶点的正方形个数

后三项可以枚举两个点,直接得出剩下两个点的位置,判断第3,4个点是否存在,是否合法来得到,判断是否存在可以用哈希

然而前两项怎么算啊-_-

随便选的我考场上找规律找出来了 ,然而只选一个点的考场上我就搞不出来了-_-

orz ljss,我们得到如下解法

对于一个边长为x的平行于坐标轴的正方形,四个顶点分别在这个正方形四条边上的正方形有x个,且这个边长为x的正方形边上的每个点都作为且仅作为那x个正方形中的一个正方形的顶点

这样的话,随便选的方案数就是sigma 边长为i的平行于坐标轴的正方形的个数×i,以i为顶点的方案数就是i在多少个平行于坐标轴的正方形上

就这么搞,把中间有些式子化简一下,就可以过了

哈希请手写链表哈希,jdfz的wfy神犇考场上用map被卡掉50分

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
using namespace std;
#define MAXN 2010
#define MAXM 1010
#define ll long long
#define INF 1000000000
#define MOD 100000007
#define eps 1e-8
ll n,m,K;
ll x[MAXN],y[MAXN];
ll ans;
ll tmp2,tmp3,tmp4;
vector<ll>hs[10000000];
ll cal1(ll x,ll y){
    if(x>y){
        return 0;
    }
    x--;
    return ((1+y)*y/2-(1+x)*x/2+y-x)%MOD;
}
ll cal2(ll x,ll y,ll h,ll p){
    if(x>y){
        return 0;
    }
    return (y-x+1)*(h-p+1)%MOD;
}
ll cal3(ll x,ll y,ll p){
    if(x>y){
        return 0;
    }
    return (y-x+1)*p%MOD;
}
ll cal4(ll x,ll y,ll h){
    if(x>y){
        return 0;
    }
    x--;
    return ((y-x)*h-(1+y)*y/2+(1+x)*x/2)%MOD;
}
ll cal(ll h,ll p,ll m){
     ll re=0;
     ll l=1,r=min(m,h);
     re+=cal1(l,min(r,min(h-p+1,p-1)-1));
     re+=cal2(max(h-p+1,l),min(p-1-1,r),h,p);
     re+=cal3(max(p-1,l),min(h-p,r),p);
     re+=cal4(max(l,max(h-p,p-2)+1),r,h);
     return re%MOD;
}
bool jud(ll x,ll y){
    return x>=1&&x<=n&&y>=1&&y<=m;
}
ll make_pair(ll x,ll y){
    if(x<1||x>n||y<1||y>m){
        return 0;
    }
    return (x-1)*m+y;
}
void push(ll x){
    hs[x%10000000].push_back(x);
}
bool vis(ll x){
    int i;
    int X=x%10000000;
    for(i=0;i<hs[X].size();i++){
        if(hs[X][i]==x){
            return 1;
        }
    }
    return 0;
}
int main(){
    int i,j,k;
    scanf("%lld%lld%lld",&n,&m,&K);
    n++;
    m++;
    for(i=1;i<=K;i++){
        scanf("%lld%lld",&x[i],&y[i]);
        x[i]++;
        y[i]++;
        push(make_pair(x[i],y[i]));
    }
    for(i=1;i<=min(n,m);i++){
        (ans+=(n-i)*(m-i)%MOD*i%MOD)%=MOD;
    }
    for(i=1;i<=K;i++){
        ll now=ans;
        (ans+=MOD-cal(n,x[i],m-y[i]))%=MOD;
        (ans+=MOD-cal(n,x[i],y[i]-1))%=MOD;
        (ans+=MOD-cal(m,y[i],n-x[i]))%=MOD;
        (ans+=MOD-cal(m,y[i],x[i]-1))%=MOD;
        (ans+=min(x[i]-1,y[i]-1))%=MOD;
        (ans+=min(n-x[i],y[i]-1))%=MOD;
        (ans+=min(x[i]-1,m-y[i]))%=MOD;
        (ans+=min(n-x[i],m-y[i]))%=MOD;
        for(j=1;j<i;j++){
            ll X=x[i]-x[j];
            ll Y=y[i]-y[j];
            if(jud(x[i]+Y,y[i]-X)&&jud(x[j]+Y,y[j]-X)){
                (ans+=1)%=MOD;
                tmp2++;
            }
            if(jud(x[i]-Y,y[i]+X)&&jud(x[j]-Y,y[j]+X)){
                (ans+=1)%=MOD;
                tmp2++;
            }
            if((x[i]-y[i]-x[j]+y[j])%2==0&&(y[j]-y[i]-x[i]+x[j])%2==0){
                ll a=(y[j]-y[i]-x[i]+x[j])/2;
                ll b=(x[i]-y[i]-x[j]+y[j])/2;
                if(jud(x[i]+a,y[i]+b)&&jud(x[j]-a,y[j]-b)){
                    (ans+=1)%=MOD;
                    tmp2++;
                }
                if(vis(make_pair(x[i]+a,y[i]+b))&&jud(x[j]-a,y[j]-b)){
                    tmp3++;
                }
                if(jud(x[i]+a,y[i]+b)&&vis(make_pair(x[j]-a,y[j]-b))){
                    tmp3++;
                }
                if(vis(make_pair(x[i]+a,y[i]+b))&&vis(make_pair(x[j]-a,y[j]-b))){
                    tmp4++;
                }
            }
            (tmp3+=vis(make_pair(x[i]+Y,y[i]-X))&&jud(x[j]+Y,y[j]-X))%=MOD;
            (tmp3+=vis(make_pair(x[j]+Y,y[j]-X))&&jud(x[i]+Y,y[i]-X))%=MOD;
            (tmp3+=vis(make_pair(x[i]-Y,y[i]+X))&&jud(x[j]-Y,y[j]+X))%=MOD;
            (tmp3+=vis(make_pair(x[j]-Y,y[j]+X))&&jud(x[i]-Y,y[i]+X))%=MOD;
            (tmp4+=(vis(make_pair(x[i]+Y,y[i]-X))&&vis(make_pair(x[j]+Y,y[j]-X)))+(vis(make_pair(x[i]-Y,y[i]+X))&&vis(make_pair(x[j]-Y,y[j]+X))))%=MOD;
        }
    }
    /*
    cout<<ans<<endl;
    cout<<tmp2<<endl;
    cout<<tmp3<<' '<<tmp3/3<<endl;
    cout<<tmp4<<' '<<tmp4/6<<endl;
    //*/
    (ans+=(tmp4/6))%=MOD;
    (ans+=MOD-tmp3/3)%=MOD;
    printf("%lld\n",ans);
    return 0;
}
 
/* 
5 2 8
1 2
0 0
2 1
3 0
5 0
4 1
3 2
1 0
 
 
*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值