zoukankan      html  css  js  c++  java
  • 题解 LOJ3284 「USACO 2020 US Open Platinum」Exercise

    对于一个排列,它的操作次数是它所有循环圈长度的(operatorname{lcm})

    因为答案是求乘积。我们可以分别计算每个质数的贡献。

    对于一个质数(p),它对答案的贡献是每个排列中,所有循环圈长度(分解质因数后)(p)的次数的最大值之和。即:

    [ans=prod_{p}p^{sum_{ ext{一个排列}}max_{ ext{一个循环圈}i}{len_i ext{里}p ext{的次数}}} ]

    考虑求指数部分,对(m-1)取模。即:

    [sum_{ ext{一个排列}}max_{ ext{一个循环圈}i}{len_i ext{里}p ext{的次数}} ]

    考虑min-max容斥。则上式可以转化为:

    [sum_{ ext{一个排列}}sum_{ ext{循环圈的一个子集}s}(-1)^{|s|+1}min_{iin s}{len_i ext{里}p ext{的次数}} ]

    最小值,可以转化为枚举一个值(x),若所有数都(geq x),则对最小值(+1)。于是上式可以写成:

    [sum_{ ext{一个排列}}sum_{ ext{循环圈的一个子集}s}(-1)^{|s|+1}sum_{x=1}^{n}prod_{iin s}[len_i ext{里}p ext{的次数}geq x] ]

    我们枚举质数(p),再枚举(x)。问题转化为,求所有排列的,所有循环圈的子集中,有多少个子集使得子集内所有循环圈的大小都是(p^x)的倍数。每个子集要带上容斥系数((-1)^{|s|+1})

    考虑DP。设子集内所有循环圈大小之和是(icdot p^x)的方案数为(dp[i])。转移时,类似于带标号无向连通图计数的方法,我们钦定一个点为一号点。枚举一号点所在的循环圈的大小为(jcdot p^x)。则可以这么转移:

    [dp[i]=sum_{j=1}^{i}(-1)cdot dp[i-j]cdot {icdot p^x-1choose jcdot p^x-1}(jcdot p^x-1)! ]

    DP部分的时间复杂度是(O(frac{n^2}{p^{2x}}))的。在外层枚举(p)(x)后,总复杂度是(n^2sum_{p}sum_{x}frac{1}{p^{2x}}leq n^2sum_{i=1}^{n}frac{1}{i^2}leqfrac{n^2pi^2}{6}),也就是(O(n^2))

    参考代码:

    在LOJ查看

    //LOJ3284
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=7500;
    int n,m,fac[MAXN+5],comb[MAXN+5][MAXN+5],p[MAXN+5],cnt_p,flag[MAXN+5];
    bool v[MAXN+5];
    int pow_mod(int x,int i,int MOD){
    	int y=1;
    	while(i){
    		if(i&1)y=(ll)y*x%MOD;
    		x=(ll)x*x%MOD;
    		i>>=1;
    	}
    	return y;
    }
    int calc(int s,int MOD){
    	static int dp[MAXN+5];
    	int lim=n/s;
    	dp[0]=MOD-1;
    	for(int i=1;i<=lim;++i){
    		dp[i]=0;
    		for(int j=1;j<=i;++j)if(dp[i-j]){//钦定一个一号点,一号点所在的环长为j*s
    			dp[i]=(dp[i]-(ll)dp[i-j]*comb[i*s-1][j*s-1]%MOD*fac[j*s-1]%MOD+MOD)%MOD;
    		}
    	}
    	int res=0;
    	for(int i=1;i<=lim;++i)res=(res+(ll)dp[i]*comb[n][i*s]%MOD*fac[n-i*s]%MOD)%MOD;
    	return res;
    }
    int main() {
    	cin>>n>>m;
    	fac[0]=1;
    	for(int i=1;i<=n;++i)fac[i]=(ll)fac[i-1]*i%(m-1);//阶乘
    	comb[0][0]=1;
    	for(int i=1;i<=n;++i){
    		comb[i][0]=1;
    		for(int j=1;j<=i;++j){
    			int& x=comb[i][j];
    			x=comb[i-1][j]+comb[i-1][j-1];
    			x=(x<(m-1)?x:x-(m-1));
    		}
    	}//组合数
    	for(int i=2;i<=n;++i){
    		if(!v[i])p[++cnt_p]=i;
    		for(int j=1;j<=cnt_p&&p[j]*i<=n;++j){
    			v[i*p[j]]=1;
    			if(i%p[j]==0)break;
    		}
    	}//质数
    	for(int i=1;i<=cnt_p;++i){
    		for(int j=p[i];j<=n;j*=p[i]){
    			flag[j]=p[i];
    		}
    	}//质数的幂
    	
    	int ans=1;
    	for(int i=1;i<=n;++i)if(flag[i]){
    		ans=(ll)ans*pow_mod(flag[i],calc(i,m-1),m)%m;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    IO模型
    Redis的cluster模式
    #4789. 啊孤独感放辣椒
    #4754. 旅途
    #3189. 八纵八横(c)
    洛谷P2624 [HNOI2008]明明的烦恼
    CF938G Shortest Path Queries
    [CTSC2010]珠宝商
    洛谷P1903 [国家集训队]数颜色 / 维护队列
    AtCoder3611 Tree MST
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12745877.html
Copyright © 2011-2022 走看看