zoukankan      html  css  js  c++  java
  • ●BZOJ 4559 [JLoi2016]成绩比较(容斥)

    题链:

    http://www.lydsy.com/JudgeOnline/problem.php?id=4559

    题解:

    容斥,拉格朗日插值法。
    结合网上的另一种方法,以及插值法,可以把本题做到 O(N2)+O(N2+logN),
    (本题的 O(N3)以及拉格朗日插值法在本题的用法,本篇目不再赘述。)
    定义 f[k]表示至少碾压 k个人的方案数(只考虑分数相对大小关系,不考虑实际分数大小)。
    image
    式子的含义是从N-1个人里面选K个人来碾压,然后对于每门科目,
    再从没被碾压的人里选一些出来使得B神在本科目的排名为 R。
    然后怎样由f[K]得到恰好有K个人被碾压的方案数ANS呢?
    套路部分:
    容斥系数如下:
    f[K]    :1
    f[K+1]    :-C(K+1,K) 
    f[K+2]    :+C(K+2,K)
    ......
    f[k+j]    :(-1)^(j)*C(k+j,k)
    这些东西加起来就得到 ANS了。
    容斥系数怎么推出来的呢? 看看这个题目的解法,一样的套路,一样的味道。
    求出了 ANS以后,
    如果设 Y[i]表示第i门课程且B神排在第R[i]名时的分数分布方案。
    image
    最后的答案就是 ANS*Y[1]*Y[2]*Y[3]*...*Y[M]
    而这个 Y[i]可以用拉格朗日插值法求出。

    代码: 

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 105
    #define _ %mod
    #define filein(x) freopen(#x".in","r",stdin);
    #define fileout(x) freopen(#x".out","w",stdout);
    using namespace std;
    const int mod=1000000007;
    int dp[MAXN],U[MAXN],R[MAXN],C[MAXN][MAXN],Y[MAXN],inv[MAXN];
    int N,M,K,ANS;
    int pow(int a,int b){
    	int now=1;
    	while(b){
    		if(b&1) now=(1ll*now*a)_;
    		a=(1ll*a*a)_; b>>=1;
    	}
    	return now;
    }
    int Lagrange(int u,int r){
    	static int lpi[MAXN],rpi[MAXN],p[MAXN],ans,tmp;
    	lpi[0]=1; rpi[N+2]=1; ans=0;
    	for(int i=1;i<=N+1;i++){
    		p[i]=(1ll*p[i-1]+1ll*pow(i,N-r)*pow(u-i,r-1)_)_;
    		if(i==u) return p[i];
    	}
    	for(int i=1;i<=N+1;i++) lpi[i]=1ll*lpi[i-1]*(u-i)_;
    	for(int i=N+1;i>=1;i--) rpi[i]=1ll*rpi[i+1]*(u-i)_;
    	for(int i=1;tmp=1,i<=N+1;i++){
    		tmp=1ll*tmp*lpi[i-1]_*rpi[i+1]_*inv[i-1]_*inv[N+1-i]_*p[i]_;
    		tmp=(1ll*tmp*((N+1-i)&1?-1:1)+mod)_;
    		ans=(1ll*ans+tmp)_;
    	}
    	return ans;
    }
    int main()
    {
    	scanf("%d%d%d",&N,&M,&K);
    	inv[0]=1; inv[1]=1; 
    	for(int i=2;i<=N+1;i++) inv[i]=((-1ll*(mod/i)*inv[mod%i])_+mod)_;
    	for(int i=1;i<=N+1;i++) inv[i]=1ll*inv[i]*inv[i-1]_;
    	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<=N;i++){
    		C[i][0]=1;
    		for(int j=1;j<=i;j++)
    			C[i][j]=(1ll*C[i-1][j-1]+C[i-1][j])_;
    	}
    	for(int i=1;i<=M;i++) Y[i]=Lagrange(U[i],R[i]);
     	for(int i=N-1;i>=K;i--)
        {
            dp[i]=C[N-1][i];
            for(int j=1;j<=M;j++)    dp[i]=1ll*dp[i]*C[N-i-1][N-R[j]-i]_;
            ANS=(1ll*ANS+(((i^K)&1)?-1:1)*1ll*dp[i]*C[i][K]_+mod)_;
        }
    	for(int i=1;i<=M;i++) ANS=1ll*ANS*Y[i]_;
    	printf("%d",(ANS+mod)_);
    	return 0;
    }

  • 相关阅读:
    【2019/5/24】周进度报告
    第一阶段意见汇总以及改进
    【2019/5/18】周进度报告
    用户体验评价-搜狗输入法
    第二阶段冲刺1
    第十四周总结
    找水王
    第十三周总结
    程序员修炼之道-从小工到专家阅读笔记02
    第十二周总结
  • 原文地址:https://www.cnblogs.com/zj75211/p/8029303.html
Copyright © 2011-2022 走看看