【jzoj4965】【Equation】【容斥】

本文介绍了一道关于考试计分的算法题目,主人公小Z在梦中回忆起考试场景并试图通过数学方法来计算所有可能的得分组合。利用隔板原理和容斥原理解决有约束条件下的组合问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

听着自己美妙的曲子,小Z进入了梦乡。在梦中,小Z仿佛又回到了自己纵横考场的年代。在梦中,小Z参加了一场考试,这场考试一共有n道题,每道题的最终得分都是一个大于等于0的整数。然而醒来后,小Z忘记了自己每道题的得分。他只记得自己计算过m次一些题目的分数和,每道题都被计算过,并且只被计算过一次。除此之外他还记得其中t道题的满分分别是多少(一道题的得分不会超过满分)。现在小Z想知道他这场考试有多少种得分情况(至少有一道题的得分不同就算不同的情况),因为这个答案可能很大,你只需要输出答案对1,000,000,007取模后的结果即可。

解题思路

考虑没有限制的情况,可以用隔板原理求出。考虑到限制很少可以用容斥原理,强制分配使其非法,用隔板原理求出,对于没限制的情况可以提前求出。

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const mxn=1e6,mo=1e9+7;
int n,m,t,ans,cnt,preans=1,tag[mxn+10],a[mxn+10][2],b[30][2],d[30][2],renum[mxn+10],bel[mxn+10],fact[mxn*2+10],ni[mxn*2+10];
int c(int x,int y){
    return 1ll*fact[x]*ni[y]%mo*ni[x-y]%mo;
}
void dfs(int now,int tag){
    if(now>t){
        int tmp=1;
        fo(i,1,cnt)
            tmp=1ll*tmp*((d[i][1]>=0)?(c(d[i][1]+d[i][0]-1,d[i][0]-1)):0)%mo;
        ans=(ans+1ll*tag*preans*tmp%mo)%mo;
        return;
    }
    d[bel[b[now][0]]][1]-=b[now][1]+1;
    dfs(now+1,-tag);
    d[bel[b[now][0]]][1]+=b[now][1]+1;
    dfs(now+1,tag);
}
int Pow(int x,int y){
    int z=1;
    while(y){
        if(y&1)z=1ll*z*x%mo;
        x=1ll*x*x%mo;
        y=y>>1;
    }
    return z;
}
int read(){
    int v=0;char ch=getchar();
    for(;(ch<'0')||(ch>'9');ch=getchar());
    for(;(ch>='0')&&(ch<='9');v=v*10+ch-'0',ch=getchar());
    return v;
}
int main(){
    //freopen("equation.in","r",stdin);
    //freopen("equation.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    n=read();m=read();int x;
    fact[0]=ni[0]=1;fo(i,1,mxn*2)fact[i]=1ll*fact[i-1]*i%mo,ni[i]=Pow(fact[i],mo-2);
    fo(i,1,m){
        a[i][0]=read();
        fo(j,1,a[i][0])x=read(),bel[x]=i;
        a[i][1]=read();
    }
    t=read();
    fo(i,1,t)b[i][0]=read(),b[i][1]=read(),tag[bel[b[i][0]]]=1;
    fo(i,1,m)if(tag[i])cnt++,d[cnt][0]=a[i][0],d[cnt][1]=a[i][1],renum[i]=cnt;
    else preans=1ll*preans*c(a[i][0]+a[i][1]-1,a[i][0]-1)%mo;
    fo(i,1,t)bel[b[i][0]]=renum[bel[b[i][0]]];
    dfs(1,1);
    printf("%d",(ans%mo+mo)%mo);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值