BZOJ 4559: [JLoi2016]成绩比较【计数dp,容斥,组合数

80 篇文章 0 订阅
6 篇文章 0 订阅

听说王队长的题解特别妙【摔


好吧的确挺♂妙


先yy出求每个人相对排名不同的方案数(用f来记录)

因为是有顺序的……所以不能直接容斥……就用 f[i] 表示 有刚好 i 个人被碾压的方案数 , 再用 至少 i 个人被碾压的方案数 减掉不合法的

看代码吧,还是挺好懂的,或者前两篇题解也写的很稳【王队长的题解啊exciting


然后求在每种排名下 分数不同的方案数……自己yy一下 C一C……xjb搞就完啦【真的不是忙着回家懒得码字了【滑稽

嘤……jq爸爸的容斥这么强于是Flaze莫名入了计数dp的坑,虽然因为太弱被jq 裱上了天2333


哼 我觉得jq马上就能上传说233333


代码↓

#include<bits/stdc++.h>
#define MAXN 105
#define MOD 1000000007ll
using namespace std;	int n,m,K;
inline int read() {
	register int ch = getchar();
	while(!isdigit(ch))	ch = getchar();
	register int rtn = 0;
	while(isdigit(ch))	rtn = rtn*10 + ch - '0' , ch = getchar();
	return rtn;
}

inline long long pow_(long long d,int c) {
	long long rtn = 1 ;
	for(;c;c>>=1,d = d*d%MOD)
		if(c&1)
			(rtn *= d) %= MOD;
	return rtn ;
}

long long u[MAXN] , rk[MAXN] , max_rank = 0 ;
long long fac[MAXN] , inv[MAXN];

long long f[MAXN] , g[MAXN];

inline long long C(int a,int b) {return fac[a]*inv[b] %MOD *inv[a-b] %MOD;}
inline long long A(int a,int b) {return fac[a]*inv[a-b] %MOD;}

int main() {
	//freopen("1.in","r",stdin);
	n = read() , m = read() , K = read();
	for (int i=1;i<=m;++i)	u[i] = (long long) read();
	for (int i=1;i<=m;++i)	
		max_rank = max( max_rank , rk[i] = (long long) read() );

	//printf("max = %lld\n",max_rank);
	if(K> n - max_rank)	return puts("0"),0;

	fac[0] = 1 , inv[0] = 1;
	for (int i=1;i<=100;++i)
		fac[i] = fac[i-1] * i % MOD , inv[i] = pow_(fac[i],MOD-2);

	for(int i=n-max_rank;i;--i){
		f[i] = C(n-1,i);
		for(int j=1;j<=m;++j)	( f[i] *= C(n-i-1,rk[j]-1) ) %= MOD;
		for(int j=n-max_rank;j^i;--j)
			( f[i] += MOD - C(j,i)*f[j]%MOD ) %= MOD;
	}

	//printf("%lld\n",f[K]);

	long long tmp = 1;
	for(int i=1;i<=m;++i){
		memset(g,0,sizeof g);
		g[0] = u[i];
		long long bbb = u[i] + 1 ;
		long long base = bbb * bbb % MOD;
		for(int k=1;k<=n;++k) {
			g[k] = base - 1 ;
			long long mns = 0;
			for(int j=0;j<k;++j)
				(mns += C(k+1,j)*g[j]%MOD) %= MOD;
			(g[k] += MOD - mns ) %= MOD;
			(g[k] *= pow_(k+1,MOD-2) ) %= MOD;
			base *= bbb;
			base %= MOD;
		}

		bbb = 0;
		for(int j=0;j<rk[i];++j){
			if(j&1)
				(bbb += MOD 
				 	- C(rk[i]-1,j) * pow_(u[i],rk[i]-1-j) % MOD * g[n-rk[i]+j] % MOD) %= MOD;
			else	(bbb += C(rk[i]-1,j) * pow_(u[i],rk[i]-1-j) % MOD * g[n-rk[i]+j]) %= MOD;
		}
		(tmp *= bbb) %= MOD;

		//printf("bbb = %lld\n",bbb);
	}

	printf("%lld\n",tmp * f[K] % MOD);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值