• bzoj1025: [SCOI2009]游戏(DP)


      题目大意:将长度为n的排列作为1,2,3,...,n的置换,有可能置换x次之后,序列又回到了1,2,3,...,n,求所有可能的x的个数。

      看见这种一脸懵逼的题第一要务当然是简化题意。。。我们可以发现,序列回到原状的次数就是每个循环的规模(即在循环中的数字个数)的lcm,而且因为有n个数,显然所有循环的规模之和就是n,那么问题就被简化成了a1+a2+a3+...+ak=n,求lcm(a1,a2,a3,...,an)的个数。

      现在题意已经清楚多了,那咋写呢QAQ

      我们把一个数分解质因数,p为质数,那么A=p1^m1*p2^m2*p3^m3*...*ph^mh,我们令a1=p1^m1,a2=p2^m2,...,ah=ph^mh,易证a1+a2+a3+...+ah<=n(分<和=两种情况讨论),则A为一个可行解。

      于是问题又变成了求有多少种a1+a2+a3+...+ah<=n。

      即有多少种(m1,m2,m3,...,mh)使p1^m1+p2^m2+p3^m3+...+ph^mh<=n。

      令f[i][j]为前i个质数,p1^m1+p2^m2+p3^m3+...+pi^mi和为j的方案数,则有:

      f[i][j]=f[i-1][j]【这个质数不用】+sigma(f[i-1][j-p[i]^k])【j-p[i]^k>=0】

      tot为n以内的质数个数,则答案为sigma(f[tot][i])【0<=i<=n】

    代码如下:

    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #define ll long long
    using namespace std;
    ll f[1010][1010],ans;
    int n,p[1010],tot;
    bool v[1010];
    int main()
    {
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
        if(!v[i])
        {
            p[++tot]=i;
            for(int j=2;j*i<=n;j++)v[i*j]=true;
        }
        f[0][0]=1;
        for(int i=1;i<=tot;i++)
        for(int j=0;j<=n;j++)
        {
            f[i][j]=f[i-1][j];
            for(int k=1,sum=1;j-sum*p[i]>=0;k++)
            {
                sum*=p[i];
                f[i][j]+=f[i-1][j-sum];
            }
        }
        for(int i=0;i<=n;i++)
        ans+=f[tot][i];
        printf("%lld
    ",ans);
    }
    View Code
  • 相关阅读:
    将Web项目War包部署到Tomcat服务器基本步骤(完整版)
    性能实战分析-环境搭建(一)
    SQL Server Profiler追踪数据库死锁
    性能测试的各种监控工具大全
    python学习
    Linux常见面试题一
    Linux下用于查看系统当前登录用户信息的4种方法
    HDU 1394 Minimum Inversion Number(线段树求逆序对)
    SGU 180 Inversions(离散化 + 线段树求逆序对)
    Codeforces Round #FF (Div. 2) C. DZY Loves Sequences
  • 原文地址:https://www.cnblogs.com/Sakits/p/6659237.html
Copyright © 2020-2023  润新知