zoukankan      html  css  js  c++  java
  • luogu P3270 [JLOI2016]成绩比较

    这题我好像理解了几天/kk。

    分三步考虑:

    第一部分计数

    由于每个人都是不一样的,所以我们首先要枚举哪 (k) 个人被碾压,贡献是一个组合数 (inom {n-1} k)(B神自己不算在内)。

    第二部分计数

    现在我们要考虑对于每一门学科,哪些人比B神高,哪些人考的成绩小于等于B神。设恰有 (i) 个人没有被B神碾压的方案数为 (f_i),至多有 (i) 个人被B神碾压的方案数为 (g_i)。答案就是 (f_{n-k-1})。我们发现:

    [g_i=sumlimits_{j=0}^i inom i j f_j ]

    根据二项式反演可得:

    [f_i=sumlimits_{j=0}^i (-1)^{i-j}inom i j g_j ]

    然后这个 (g_i) 其实也非常好算:

    [g_i=prodlimits_{i=1}^m inom {n-k-1}{r_i-1} ]

    此部分时间复杂度 (O(n^2))

    第三部分计数

    现在对于每一门课,我们知道了每个人与B神相比的状态,只要再枚举分数就好了。设 (M_i) 表示第 (i) 门课的方案数(最后用乘法原理乘起来),暴力枚举B神的分数:

    [egin{aligned} M_i&=sumlimits_{i=1}^{U_i}(U_i-i)^{r_i-1} imes i^{n-r_i}\ &=sumlimits_{i=1}^{U_i} sumlimits_{j=1}^{r_i-1}inom {r_i-1} jU_i^j imes (-1)^{r_i-j-1} imes i^{n-j-1}\ &=sumlimits_{j=1}^{r_i-1}inom{r_i-1}{j}U_i^j imes (-1)^{r_i-j-1}sumlimits_{i=1}^{U_i} i^{n-j-1} end{aligned} ]

    发现后面那个东东是自然数幂和,所以可以拉格朗日插值直接爆算,时间复杂度 (O(n^3))

    最后把三个部分乘起来就行了qwq。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #define int long long
     
    using namespace std;
    
    const int N=1009,M=1000000007;
    int n,m,k,c[N][N],U[N],R[N],pre[N],suf[N],fac[N],inv_fac[N],d[N],cnt,a[N],p[N];
    
    int ksm(int a,int b)
    {
    	int res=1;
    	while(b)
    	{
    		if(b&1)
    			res=res*a%M;
    		b>>=1,a=a*a%M;
    	}
    	return res;
    }
    
    void init()
    {
    	scanf("%lld %lld %lld",&n,&m,&k);
    	for (int i=1;i<=m;i++)
    		scanf("%d",&U[i]);
    	for (int i=1;i<=m;i++)
    		scanf("%d",&R[i]);
    	for (int i=0;i<=100;i++)
    		c[i][0]=1;
    	for (int i=1;i<=100;i++)
    		for (int j=1;j<=i;j++)
    			c[i][j]=(c[i-1][j]+c[i-1][j-1])%M;
    }
    
    void prework(int k)
    {
    	d[1]=1;cnt=0;
    	for (int i=2;i<=k+5;i++)
    		a[i]=0;
    	for (int i=2;i<=k+5;i++)
    	{
    		if(!a[i])
    			a[i]=i,p[++cnt]=a[i],d[i]=ksm(i,k);
    		for (int j=1;j<=cnt;j++)
    		{
    			if(p[j]>a[i]||p[j]>(k+5)/i)
    				break;
    			a[p[j]*i]=p[j],d[p[j]*i]=d[p[j]]*d[i]%M;
    		}
    	}
    	for (int i=2;i<=k+5;i++)
    		d[i]=(d[i]+d[i-1])%M;
    }
    
    int calc(int x,int k)
    {
    	prework(k-1);
    	pre[0]=suf[k+2]=1;
    	for (int i=1;i<=k+1;i++)
    		pre[i]=pre[i-1]*(x-i)%M;
    	for (int i=k+1;i>=1;i--)
    		suf[i]=suf[i+1]*(x-i)%M;
    	fac[0]=1;
    	for (int i=1;i<=k+1;i++)
    		fac[i]=fac[i-1]*i%M;
    	inv_fac[k+1]=ksm(fac[k+1],M-2);
    	for (int i=k;i>=0;i--)
    		inv_fac[i]=inv_fac[i+1]*(i+1)%M;
    	int ans=0;
    	for (int i=1;i<=k+1;i++)
    		ans=(ans+d[i]*pre[i-1]%M*suf[i+1]%M*inv_fac[i-1]%M*inv_fac[k+1-i]%M*((k+1-i&1)?-1:1))%M;
    	return ans;
    }
    
    void work()
    {
    	int ans1=0,ans2=1,d=n-k-1;
    	for (int i=d;i>=0;i--)
    	{
    		int tmp=((i-d)&1)?-1:1;
    		for (int j=1;j<=m;j++)
    			tmp=tmp*c[i][R[j]-1]%M;
    		ans1=(ans1+tmp*c[d][i]%M)%M;
    	}
    	for (int i=1;i<=m;i++)
    	{
    		int tmp=0;
    		for (int k=0;k<=R[i]-1;k++)
    			tmp=(tmp+ksm(U[i],k)*calc(U[i],n-k)%M*c[R[i]-1][k]%M*((R[i]-1-k&1)?-1:1))%M;
    		ans2=ans2*tmp%M;
    	}
    	printf("%lld
    ",(c[n-1][k]*ans1%M*ans2%M+M)%M);
    }
    
    signed main()
    {
    	init();
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    L1-046. 整除光棍
    L2-014. 列车调度
    L2-009. 抢红包
    L2-005. 集合相似度
    L2-021. 点赞狂魔
    L1-033. 出生年
    设计模式之生成器模式
    设计模式之抽象工厂模式
    设计模式之工厂方法模式
    设计模式之简单工厂模式
  • 原文地址:https://www.cnblogs.com/With-penguin/p/13521131.html
Copyright © 2011-2022 走看看