6545. 【GDOI2020模拟4.8】USACO 2020 Open Contest, Platinum(exercise)

题目描述

Farmer John(又)想到了一个新的奶牛晨练方案! 如同之前,Farmer John 的 N 头奶牛( 1 ≤ N ≤ 7500 )站成一排。对于 1 ≤ i ≤ N 的每一个 i ,从左往右第 i 头奶牛的编号为 i 。他告诉她们重复以下步骤,直到奶牛们与她们开始时的顺序相同。给定长为 N 的一个排列 A ,奶牛们改变她们的顺序,使得在改变之前从左往右第 i 头奶牛在改变之后为从左往右第 A i 头。 例如,如果A=(1,2,3,4,5),那么奶牛们总共进行一步就回到了同样的顺序。如果A=(2,3,1,5,4),那么奶牛们总共进行六步之后回到起始的顺序。每步之后奶牛们从左往右的顺序如下:
计算所有可能的 N ! 种长为 N 的排列 A 回到起始顺序需要的步数的乘积。

n<=7500

题解

直接算的话lcm太大了,考虑算p^k的贡献

\(\huge ans=\prod{p^{\sum{f(p^k)}}}\)

其中f(x)表示至少存在一个长度为x的倍数的环的方案数

容斥一下,系数为(-1)^环的个数

每次硬点当前环的开头为1号点,剩下的组合数排一下,最后乘上(环长-1)!表示环的连边方案

时间为\(\sum{(\frac{n}{x})^2}\),其中\(\sum{\frac{1}{x^2}}=\frac{\pi^2}{6}\),所以时间为O(n^2)

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
#define file
using namespace std;

ll jc[7501],C[7501][7501],g[7501],s,ans;
int p[7501],n,mod,i,j,k,l,len;
bool f[7501];

ll qpower(ll a,int b) {ll ans=1;while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}

void init()
{
	int i,j,k,l;
	
	fo(i,2,n)
	{
		if (!f[i]) p[++len]=i;
		
		fo(j,1,len)
		if (i*p[j]<=n)
		{
			f[i*p[j]]=1;
			if (!(i%p[j])) break;
		}
		else break;
	}
}

ll js(int s)
{
	int N=n/s,i,j,k,l;
	ll sum=0;
	
	memset(g,0,(N+1)*8);g[0]=1;
	fo(i,1,N)
	{
		fo(j,1,i)
		g[i]=(g[i]-g[i-j]*C[i*s-1][j*s-1]%(mod-1)*jc[j*s-1])%(mod-1);
	}
	
	fo(i,0,N) sum=(sum+g[i]*C[n][i*s]%(mod-1)*jc[n-i*s])%(mod-1);
	return (jc[n]-sum)%(mod-1);
}

int main()
{
	freopen("exercise.in","r",stdin);
	#ifdef file
	freopen("exercise.out","w",stdout);
	#endif
	
	scanf("%d%d",&n,&mod);init();
	jc[0]=1;fo(i,1,n) jc[i]=jc[i-1]*i%(mod-1);
	fo(i,1,n)
	{
		C[i][0]=C[i][i]=1;
		fo(j,1,i-1) C[i][j]=(C[i-1][j]+C[i-1][j-1])%(mod-1);
	}
	
	ans=1;
	fo(i,1,len)
	{
		for (s=0,j=p[i]; j<=n; j*=p[i]) s=(s+js(j))%(mod-1);
		ans=ans*qpower(p[i],(s+mod-1)%(mod-1))%mod;
	}
	printf("%lld\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
posted @ 2020-04-12 12:09  gmh77  阅读(300)  评论(0编辑  收藏  举报