BZOJ 1025 游戏【置换群】

【题意】略,中文题可自己查看!

【分析】在做题之前介绍一下置换群的基本概念。。。置换的概念是什么?一个有限集合的一一变换叫做置换,一对对置换组成了置换群。对于一个集合a(a[1],a[2],a[3]...a[n]) 通过置换可以变成 (b[a[1]],b[a[2]],b[a[3]]...b[a[n]]) b的作用就是置换(可以理解为某种函数的作用),将原来的集合映射成具有相应次序的集合a',a'可以看做是a的相同元素集合,不同的排列组合的一个集合。 每个n元的置换都可以表示成若干个互不相交的循环置换的乘积,设每个子循环置换的循环节为ci,则总的置换的循环节显然为lcm(c1,c2..cn)

好了,那么这个题一眼就可以看出是典型的置换群了。顾而

=lcm{Ai,Ai},i=1kAi=n
因此这个问题转化为了,把一个数n拆分成若干个数,问这若干个数的最小公倍数的可能情况数!计数问题,典型的dp转移!

【状态表示】

筛素数表,dp[i][j]表示j拆分的若干个数分解质因数之后最大质因数不超过第i个质因数的最小公倍数可能性。

dp[i][j]=dp[i-1][j]+sigma{dp[i][j-p[i]^k]}

[AC代码]

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
bool used[1005];
int prime[1005];
int num,n;
ll dp[1005][1005];//i个质数和为j的总方案数
void make_tab(int x)
{
    memset(used,false,sizeof(used));
    for(int i=2; i<=x; i++)
    {
        if(!used[i])
        {
            prime[++num]=i;
            for(int j=i*i; j<=x; j+=i)
            {
                used[j] = true;
            }
        }
    }
}
void solve()
{
    dp[0][0]=1;
    for(int i=1; i<=num; i++)
    {
        for(int j=0; j<=n; j++)
        {
            dp[i][j] = dp[i-1][j];
        }
        for(int j=prime[i]; j<=n; j*=prime[i])
        {
            for(int k=0; k<=n-j; k++)
            {
                dp[i][j+k]+=dp[i-1][k];
            }
        }
    }
    ll ans = 0;
    for(int i=0; i<=n; i++)
    {
        ans+=dp[num][i];
    }
    cout<<ans<<endl;
}
int main()
{
    cin>>n;
    make_tab(n);
    solve();
    return 0;
}



  

因此

你可能感兴趣的:(BZOJ 1025 游戏【置换群】)