【题意分析】

  定义一个等价类为满足如下条件的一个极大的集合Q:∀t∈Q,k∈N+,若tk∈全集R,都成立tk∈Q。

  给定n,记[1,n]∩N上所有排列置换的全集为R。求对于所有的等价类Q,card({x|x=card(Q),Q∈R})。

【解题思路】

  很明显,一个排列置换能分解成一个或几个不相交的置换环,其所在等价类的元素个数即为所有置换环长度的最小公倍数。

  显然,若一个置换所有置换环长度的最大公约数大于1,则一定有一个置换环长度的最大公约数等于1的所在等价类元素个数与之相同。

  所以,我们只要统计只有互质且不等于1的长度的置换环的置换所在等价类元素个数即可。

  这样问题就可以转化为如何拆分n使所有拆分出的数都是pk(p为互不相等的质数,k∈[1,+∞)∩N)。

  先筛出[1,n]∩N范围内所有质数,然后DP,f[i][j]表示已经选到了第i个质数,可分配的长度还剩下j的剩余时的拆分数。

  转移方程:f[i][j]=f[i-1][j]+Σf[i-1][j+p[i]k],时间复杂度O(nπ(n))。

【参考代码】

 1 #pragma GCC optimize(2)
 2 #pragma comment(linker,"/STACK:1024000000,1024000000")
 3 #include <cstdio>
 4 #include <cstring>
 5 #define REP(i,low,high) for(register int i=(low);i<=(high);++i)
 6 using namespace std;
 7  
 8 static int n,cnt=0; long long f[1010][1010]; bool isprime[1010]; int prime[1010];
 9  
10 long long DFS(const int &now,const int &rest)
11 {
12     if(now>cnt) return 1; if(~f[now][rest]) return f[now][rest];
13     f[now][rest]=DFS(now+1,rest);
14     for(register int i=prime[now];i<=rest;i*=prime[now])
15     {
16         f[now][rest]+=DFS(now+1,rest-i);
17     }
18     return f[now][rest];
19 }
20  
21 int main()
22 {
23     scanf("%d",&n),memset(isprime,1,sizeof isprime),memset(f,-1,sizeof f);
24     REP(i,2,n) if(isprime[i]) {REP(j,2,n/i) isprime[i*j]=0; prime[++cnt]=i;}
25     return printf("%lld\n",DFS(1,n)),0;
26 }
View Code

 

We Secure, We Contain, We Protect.