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; }