【题意分析】
定义一个等价类为满足如下条件的一个极大的集合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 }
We Secure, We Contain, We Protect.