BZOJ5262(容斥)

题目描述
听着自己美妙的曲子,小Z进入了梦乡。在梦中,小Z仿佛又回到了自己纵横考场的年代。在梦中,小Z参加了一场
考试,这场考试一共有n道题,每道题的最终得分都是一个大于等于0的整数。然而醒来后,小Z忘记了自己每道题
的得分。他只记得自己计算过m次一些题目的分数和,每道题都被计算过,并且只被计算过一次。除此之外他还记
得其中t道题的满分分别是多少(一道题的得分不会超过满分)。现在小Z想知道他这场考试有多少种得分情况(至
少有一道题的得分不同就算不同的情况),因为这个答案可能很大,你只需要输出答案对1,000,000,007取模后的
结果即可。
题解
看到t比较小,就想到容斥。
我是把每一次求和分开算,最后乘起来,每次找出这次求和的所有限制,然后就2^n枚举限制的选择情况,用C(n+m-1,m-1)算方案数。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 2000009
using namespace std;
typedef long long ll;
const int maxn=2000000;
vector<ll>vec[N];
ll jie[N],ni[N],ans,k[N],sum[N],n,m,p[N],t,num[N];
const int mod=1e9+7;
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline ll power(ll x,int y){
    ll ans=1;
    while(y){if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;}
    return ans;
}
inline ll C(int x,int y){if(x<y)return 0;return jie[x]*ni[y]%mod*ni[x-y]%mod;}
void dfs(int s,int i,int num,int k,int q,ll sum){
    if(i>k){
        if(s&1)ans-=C(sum-num+q-1,q-1);
        else ans+=C(sum-num+q-1,q-1);
        ans=(ans%mod+mod)%mod;
        return;
    }
    dfs(s+1,i+1,num+p[i]+1,k,q,sum);dfs(s,i+1,num,k,q,sum);
}
int main(){
//    freopen("Equation.in","r",stdin);
//    freopen("Equation.out","w",stdout);
    n=rd();m=rd();int x,y;
    jie[0]=1;for(int i=1;i<=maxn;++i)jie[i]=jie[i-1]*i%mod;ni[maxn]=power(jie[maxn],mod-2);
    for(int i=maxn-1;i>=0;--i)ni[i]=ni[i+1]*(i+1)%mod;
    for(int i=1;i<=m;++i){
        k[i]=rd();
        for(int j=1;j<=k[i];++j)x=rd(),vec[i].push_back(x);sum[i]=rd();
    }
    t=rd();
    memset(num,-1,sizeof(num));
    for(int i=1;i<=t;++i){
        x=rd();y=rd();num[x]=y;
    }ll Ans=1;
    for(int i=1;i<=m;++i){
        int q=0;
        ans=0;
        for(int j=0;j<k[i];++j)if(~num[vec[i][j]])p[++q]=num[vec[i][j]];
        dfs(0,1,0,q,k[i],sum[i]);
        (Ans*=ans)%=mod;
    }
    cout<<Ans;
    return 0;
}
原文地址:https://www.cnblogs.com/ZH-comld/p/10236873.html