Description
听着自己美妙的曲子,小Z进入了梦乡。在梦中,小Z仿佛又回到了自己纵横考场的年代。在梦中,小Z参加了一场考试,这场考试一共有n道题,每道题的最终得分都是一个大于等于0的整数。然而醒来后,小Z忘记了自己每道题的得分。他只记得自己计算过m次一些题目的分数和,每道题都被计算过,并且只被计算过一次。除此之外他还记得其中t道题的满分分别是多少(一道题的得分不会超过满分)。现在小Z想知道他这场考试有多少种得分情况(至少有一道题的得分不同就算不同的情况),因为这个答案可能很大,你只需要输出答案对1,000,000,007取模后的结果即可。
Data Constraint
对于30%的数据:n,c≤8。
对于另外40%的数据:t=0。
对于100%的数据:1≤n,m≤1,000,000,0≤c,L≤1,000,000,0≤t≤20。
Solution
在考场上看漏了条件:每道题都只被计算过一次!!!也就是说,我们可以很轻松的解决70分的数据。
对于t=0的情况,我们直接对于m个限制求一下相乘即可。即若当前限制为有t个题目,得分为sum,那么就等同于将这sum分随机分入t个题目中,那么方案显然为
Ct−1sum+t−1
。
那现在讨论有限制最高得分的情况。我们可以将限制最高得分放入m个限制中的对应限制,做一下递推。设f[i][j]表示做到当前的限制x中的有最高得分限制的第i个题目,前i个有得分限制的题目的得分为j的方案数。那么
f[i][j]=∑jk=j−limit[i]f[i−1][k]
,这个显然可以用前缀和优化,变成
f[i][j]=g[i−1][j]−g[i−1][j−limit[i]−1]
。由于dp仅有有最高得分限制的点有关,所以这是O(T*L)的。接下来我们只要对剩下的得分随机分配到没有最高得分限制的点中即可,即
∑score[x]j=0f[num[x]][j]∗Cnum1[x]−num[x]−1score[x]−j+num1[x]−num[x]−1
。总时间复杂度O(
L∗T∗logN
)。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=1e6+5,mo=1e9+7;
int first[maxn],last[maxn],next[maxn],a[maxn],b[maxn],d[maxn],p[maxn];
int f[21][maxn],g[21][maxn];
ll n,m,i,t,j,k,l,x,y,z,num,q,ans,fact[2*maxn];
void lian(ll x,ll y){
last[++num]=y;next[num]=first[x];first[x]=num;
}
ll mi(ll x,ll y){
if (y==1) return x;
ll t=mi(x,y/2);
if (y%2)return t*t%mo*x%mo;return t*t%mo;
}
ll dg(ll x,ll y){
if (y<0) return 1;
return fact[x]*mi(fact[y]*fact[x-y]%mo,mo-2)%mo;
}
int main(){
freopen("equation.in","r",stdin);freopen("equation.out","w",stdout);
scanf("%lld%lld",&n,&m);fact[0]=1;
for (i=1;i<2*maxn;i++)
fact[i]=fact[i-1]*i%mo;
for (i=1;i<=m;i++){
scanf("%lld",&t);p[i]=t;
for (j=1;j<=t;j++)
scanf("%lld",&x),lian(i,x);
scanf("%lld",&d[i]);
}
scanf("%lld",&q);
for (i=1;i<=q;i++)
scanf("%lld%lld",&x,&y),a[x]=y,b[x]=1;
for (i=0;i<maxn;i++)
g[0][i]=1;
ans=1;
for (i=1;i<=m;i++){k=0;
for (t=first[i];t;t=next[t]){
x=last[t];
if (b[x]){
k++;
for (j=0;j<=d[i];j++){
if (j-a[x]>0) f[k][j]=(g[k-1][j]-g[k-1][j-a[x]-1]+mo)%mo;
else f[k][j]=g[k-1][j];
if (j) g[k][j]=(g[k][j-1]+f[k][j])%mo;
else g[k][j]=f[k][j];
}
}
}
if (k){
t=0;
if (k!=p[i]){
for (j=0;j<=d[i];j++){
t=(t+f[k][j]*dg(d[i]-j+p[i]-k-1,p[i]-k-1)%mo+mo)%mo;
if (!f[k][j]) break;
}
}else t=f[k][d[i]];
ans=ans*t%mo;
}else ans=ans*dg(d[i]+p[i]-1,p[i]-1)%mo;
}
printf("%lld\n",ans);
}