zoukankan      html  css  js  c++  java
  • 洛谷 P6276

    洛谷题面传送门

    废了,又不会做/ll

    orz czx 写的什么神仙题解,根本看不懂(%%%%%%%%%

    首先显然一个排列的贡献为其所有置换环的乘积。考虑如何算之。

    碰到很多数的 LCM 之积只有两种可能,一是 Min-Max 容斥将 LCM 转化为 GCD,而是枚举质因子及其次数算贡献。但对于此题而言前者不是太可做(可能有复杂度不错(大概 (n^2d(n))?)的解法,不过我没有细想所以也不太清楚),因此考虑后者。

    考虑用类似于差分的思想,对于每个质因子 (p) 的每个次数 (k),我们考虑计算一下存在一个置换环大小为 (p^k) 的排列个数 (c),那么我们令答案乘以 (p^c),不难发现对于一个排列,如果其置换环大小中 (p) 次数最多的一项的次数为 (k'),那么它的贡献会在 (k=1,2,3,cdots,k') 处各被算一次,因此这样计算恰好可以算到所有置换环的贡献。于是限制问题转化为,如何求有多少个排列,满足其至少有一个置换环大小为 (x=p^k) 的倍数。

    考虑从反面入手,即计算有多少个长度为 (n) 的排列不含任何长度为 (x) 的置换环,设其为 (f_n)。那么这个子问题的答案就是 (n!-f_n)。那么如何求 (f_i) 呢?我们再从反面入手,计算一下含有长度为 (x) 的置换环的排列个数(为什么要一来一回搞两次反面呢?因为直接做递推式中需要用到 (f_i)),那么我们枚举长度为 (x) 的置换环的长度之和 (j),设 (g_j) 表示有多少个长度为 (j) 的排列满足其每个置换环大小都是 (x) 的倍数,那么有 (f_i=i!·sumlimits_{jin[1,i]}dbinom{i}{j}g_jf_{i-j}),组合数表示分配 (j) 个数给大小是 (x) 的倍数的置换环的方案数,一目了然。接下来考虑如何求 (g_i),我们枚举 (1) 所在的置换环的大小 (j),类似地有 (sumlimits_{jin[1,i]}g_{i-j}dbinom{i-1}{j-1}(j-1)!)。注意到只有 (xmid i)(g_i e 0),因此有用的 (f,g) 各只有 (dfrac{n}{x}) 个,也就保证了复杂度。这部分可能可以容斥,不过没有细想(

    总复杂度 (sumlimits_{i=1}^ndfrac{n^2}{i^2}=mathcal O(n^2)),因为 (sumlimits_{n>0}dfrac{1}{n^2}<2)

    const int MAXN=7500;
    int n,mod,phi,c[MAXN+5][MAXN+5],fac[MAXN+5];
    int qpow(int x,int e){
    	int ret=1;
    	for(;e;e>>=1,x=1ll*x*x%mod) if(e&1) ret=1ll*ret*x%mod;
    	return ret;
    }
    int pr[MAXN/5+5],prcnt=0,vis[MAXN+5];
    void sieve(int n){
    	for(int i=2;i<=n;i++){
    		if(!vis[i]) pr[++prcnt]=i;
    		for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
    			vis[pr[j]*i]=1;if(i%pr[j]==0) break;
    		}
    	}
    }
    int f[MAXN+5],g[MAXN+5];
    int calc(int x){
    	memset(f,0,sizeof(f));memset(g,0,sizeof(g));g[0]=1;
    	for(int i=x;i<=n;i+=x) for(int j=x;j<=i;j+=x) g[i]=(g[i]+1ll*c[i-1][j-1]*g[i-j]%phi*fac[j-1])%phi;
    //	for(int i=x;i<=n;i+=x) printf("%d %d
    ",i,g[i]);
    	for(int i=n%x;i<=n;i+=x){
    		f[i]=fac[i];
    		for(int j=x;j<=i;j+=x) f[i]=(f[i]-1ll*f[i-j]*g[j]%phi*c[i][j]%phi+phi)%phi;
    	} return (fac[n]-f[n]+phi)%phi;
    }
    int main(){
    	scanf("%d%d",&n,&mod);phi=mod-1;sieve(n);
    	for(int i=(fac[0]=1);i<=n;i++) fac[i]=1ll*fac[i-1]*i%phi;
    	for(int i=0;i<=n;i++){
    		c[i][0]=1;
    		for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%phi;
    	} int res=1;
    //	for(int i=1;i<=n;i++) printf("%d
    ",calc(i));
    	for(int i=1;i<=prcnt;i++) for(int j=pr[i];j<=n;j*=pr[i])
    		res=1ll*res*qpow(pr[i],calc(j))%mod;
    	printf("%d
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    [leetcode] 48. 旋转图像(Java)(模拟)
    [leetcode] 47. 全排列 II
    [leetcode] 46. 全排列(Java)
    [leetcode] 45. 跳跃游戏 II(Java)(动态规划)
    [leetcode] 875. 爱吃香蕉的珂珂(周赛)
    [leetcode] 874. 行走机器人模拟(周赛)
    《数据结构与算法分析:C语言描述》复习——第八章“并查集”——并查集
    《数据结构与算法分析:C语言描述》复习——第六章“排序”——基数排序
    《数据结构与算法分析:C语言描述》复习——第六章“排序”——桶排序
    《数据结构与算法分析:C语言描述》复习——第六章“排序”——快速排序
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P6276.html
Copyright © 2011-2022 走看看