【JZOJ4965】【GDKOI2017模拟1.21】Equation

67 篇文章 1 订阅
55 篇文章 0 订阅

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个题目中,那么方案显然为 Ct1sum+t1
那现在讨论有限制最高得分的情况。我们可以将限制最高得分放入m个限制中的对应限制,做一下递推。设f[i][j]表示做到当前的限制x中的有最高得分限制的第i个题目,前i个有得分限制的题目的得分为j的方案数。那么 f[i][j]=jk=jlimit[i]f[i1][k] ,这个显然可以用前缀和优化,变成 f[i][j]=g[i1][j]g[i1][jlimit[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( LTlogN )。

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);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值