黑暗 - 容斥 - 组合计数

题目大意:平面上a个白色棋子,b个黄色,c个蓝色,对每个棋子移动m步,每步可以上下左右移动1单位距离,问有多少种方案,使得任意两点坐标相同当且进当颜色相同。坐标绝对值、棋子总数、步数均不超过1000。
题解:先容斥不同颜色坐标不同,问题转为一些点m步后走到同一个点的方案数,将(x,y)坐标变换为(x+y,x-y),然后横纵独立,然后坐标范围不大,暴力枚举组合数计算即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 1000000007
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn() { int x;scanf("%d",&x);return x; }
const int N=100010;int fac[N],facinv[N],a[N];
inline int gabs(int x) { return x<0?-x:x; }
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
inline int prelude(int n)
{
    rep(i,fac[0]=1,n) fac[i]=(lint)fac[i-1]*i%mod;
    facinv[n]=fast_pow(fac[n],mod-2);
    for(int i=n-1;i>=0;i--) facinv[i]=(i+1ll)*facinv[i+1]%mod;
    return 0;
}
inline int binom(int n,int m) { if(n<0||m<0||n<m) return 0;return (lint)fac[n]*facinv[m]%mod*facinv[n-m]%mod; }
inline int calc(int *a,int n,int p,int m)
{
    int ans=1;
    rep(i,1,n)
    {
        int dx=gabs(a[i]-p);
        if(((dx+m)%2)||dx>m) return 0;
        ans=(lint)ans*binom(m,(dx+m)/2)%mod;
    }
    return ans;
}
inline int solve(vector<int> x,int n,int m)
{
    rep(i,1,n) a[i]=x[i];int mn=a[1],mx=a[1];
    rep(i,1,n) mn=min(mn,a[i]),mx=max(mx,a[i]);
    lint ans=0;rep(i,mx-m,mn+m) ans+=calc(a,n,i,m);
    return ans%mod;
}
struct P{
    vector<int> x,y;int n;static int m;
    inline int init(int _n)
    {
        n=_n,x.resize(n+1),y.resize(n+1);
        rep(i,1,n) x[i]=inn(),y[i]=inn(),
            x[i]+=y[i],y[i]=x[i]-2*y[i];
        return 0;
    }
    inline P& operator=(const P &p) { x=p.x,y=p.y,n=p.n;return *this; }
    inline P operator+(const P &p)const
    {
        P q;q.x=x,q.y=y,q.n=n+p.n;
        rep(i,1,p.n) q.x.pb(p.x[i]),q.y.pb(p.y[i]);
        return q;
    }
    inline int F()const { return (lint)solve(x,n,m)*solve(y,n,m)%mod; }
}A,B,C;
int P::m=0;
int main()
{
    prelude(N-1);
    int n=inn(),a=inn(),b=inn(),c=n-a-b;P::m=inn();
    A.init(a),B.init(b),C.init(c),a=A.F(),b=B.F(),c=C.F();
    int ab=(A+B).F(),bc=(B+C).F(),ac=(A+C).F(),abc=(A+B+C).F();
    lint ans=(lint)a*b%mod*c%mod-(lint)ab*c%mod-(lint)ac*b%mod-(lint)bc*a%mod+2ll*abc%mod;
    ans%=mod,ans+=mod,ans%=mod;return !printf("%lld\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值