zoukankan      html  css  js  c++  java
  • SCOI2009游戏

    1025: [SCOI2009]游戏

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 1065  Solved: 673
    [Submit][Status]

    Description

    windy学会了一种游戏。对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。最开始windy把数字按顺序1,2,3,……,N写一排在纸上。然后再在这一排下面写上它们对应的数字。然后又在新的一排下面写上它们对应的数字。如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下 1 2 3 4 5 6 2 3 1 5 4 6 3 1 2 4 5 6 1 2 3 5 4 6 2 3 1 4 5 6 3 1 2 5 4 6 1 2 3 4 5 6 这时,我们就有若干排1到N的排列,上例中有7排。现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。

    Input

    包含一个整数,N。

    Output

    包含一个整数,可能的排数。

    Sample Input

    【输入样例一】
    3


    【输入样例二】
    10

    Sample Output

    【输出样例一】
    3

    【输出样例二】
    16


    【数据规模和约定】
    30%的数据,满足 1 <= N <= 10 。
    100%的数据,满足 1 <= N <= 1000 。

    HINT

    题解:

    题目所描述的对应变换关系实质就是一个置换
    而任意一个置换都可以写成若干轮换的乘积。如题目所述1->2  2->3  3->1  4->5  5->4  6->6,可以写成:
    (1 2 3)(4 5)(6) 
    排数实际上就是若干循环节长度的最小公倍数。这里就是:3*2*1
    因此所求即是:加和恰好为N的若干正整数的最小公倍数的可能数。
    首先,由于1不影响最小公倍数(若干个整数里面就有可能有1),所以,问题转化为:
    加和小于等于N的若干正整数的最小公倍数的可能数。
    对于a1+a2+a3+a4+...+ai<=N里,任意一个正整数ai都可以表示为若干个素数的乘积  ps:我想到这儿就不会了……
    对于这若干个素数:乘积>=和
    所以问题就转化为:加和小于等于N的若干只含一个质因数的正整数的最小公倍数的可能数。
    然后先弄一个素数表。
    用dp[i,j]表示前i个素数(和它的k次幂)之和小于等于j时有多少个最小公倍数
    则答案为dp[num,n](num为小于等于N的素数个数)
    方程为
    dp[i,j]:=sum(dp[i-1,j-sushu[i]^k])注意k>=0且sushu[i]^k<=j
    这个公式不完全对,当k=0的时候要特殊处理!这个时候加的值不是dp[i-1,j-1]而是dp[i-1,j]。

    实际上,问题到了后面,也就是到了将问题转化为加和小于等于N的若干只含一个质因数的正整数的最小公倍数的可能数。

    这俨然已经成为了一个背包问题求构成种数的问题。

    有一个容量为n个背包,有很多物体,它们的体积分别问:

    1,2,2*2,2*2*2,……3,3*3,3*3*3……

    并且是01背包问题(除了1可以无限用),你想放两个2*2,和放一个2*2再放4个1的最小公倍数不是一样的吗?枚举到一个上限即可

    下面我将给出三份代码:

    一、vijosp1071新年趣事之打牌(这是我第一次接触背包问题求构成种数的问题,所以文件里有记录)

    题目附在另一篇文章里

    代码如下:

     1 var n,i,j,tot,ans:longint;
     2     a,f,g:array[0..10000] of longint;
     3     p:array[0..10000]of boolean;
     4 begin
     5 readln(tot);
     6 readln(n);f[0]:=1;
     7 for i:=1 to n do readln(a[i]);
     8 for i:=1 to n do
     9  for j:=tot downto a[i] do
    10   begin
    11   if (f[j-a[i]]>0) and (f[j]=0) then g[j]:=i;
    12   f[j]:=f[j]+f[j-a[i]];
    13   end;
    14 i:=tot;
    15 if f[tot]>1 then begin writeln('-1');halt;end;
    16 if f[tot]=0 then begin writeln('0');halt;end;
    17 while (i>0) and (g[i]<>0) do
    18  begin
    19  p[g[i]]:=true;
    20  i:=i-a[g[i]];
    21  end;
    22 for i:=1 to n do if not p[i] then write(i,' ');
    23 end.                  
    View Code

    二、本题二维做法,比较费空间

    代码:

     1 var i,j,k,n,tot,tmp:longint;
     2     check:array[0..1500] of boolean;
     3     p:array[0..1500] of longint;
     4     f:array[0..1500,0..1500] of int64;
     5 procedure getprimes;
     6  var i,j,k:longint;
     7  begin
     8  tot:=0;
     9  fillchar(check,sizeof(check),true);
    10  for i:=2 to n do
    11   begin
    12   if check[i] then begin inc(tot);p[tot]:=i;end;
    13   for j:=1 to tot do
    14    begin
    15    k:=i*p[j];
    16    if k>n then break;
    17    check[k]:=false;
    18    if i mod p[j]=0 then break;
    19    end;
    20   end;
    21  end;
    22 procedure main;
    23  begin
    24  readln(n);
    25  getprimes;
    26  for i:=0 to n do f[0,i]:=1;
    27  for i:=0 to tot do f[i,0]:=1;
    28  for i:=1 to tot do
    29   begin
    30   for j:=1 to n do
    31    begin
    32    inc(f[i,j],f[i-1,j]);
    33    k:=p[i];
    34    while k<=j do
    35     begin
    36     inc(f[i,j],f[i-1,j-k]);
    37     k:=k*p[i];
    38     end;
    39    end;
    40   end;
    41  writeln(f[tot,n]);
    42  end;
    43 begin
    44  main;
    45 end.       
    View Code

    三、本题一维做法

    代码:

     1 var i,j,k,n,tot,tmp:longint;
     2     check:array[0..1500] of boolean;
     3     p:array[0..1500] of longint;
     4     f:array[0..1500] of int64;
     5 procedure getprimes;
     6  var i,j,k:longint;
     7  begin
     8  tot:=0;
     9  fillchar(check,sizeof(check),true);
    10  for i:=2 to n do
    11   begin
    12   if check[i] then begin inc(tot);p[tot]:=i;end;
    13   for j:=1 to tot do
    14    begin
    15    k:=i*p[j];
    16    if k>n then break;
    17    check[k]:=false;
    18    if i mod p[j]=0 then break;
    19    end;
    20   end;
    21  end;
    22 procedure main;
    23  begin
    24  readln(n);
    25  getprimes;
    26  for i:=0 to n do f[i]:=1;
    27  for i:=1 to tot do
    28   for j:=n downto 1 do
    29    begin
    30    k:=p[i];
    31    while k<=j do
    32     begin
    33     inc(f[j],f[j-k]);
    34     k:=k*p[i];
    35     end;
    36    end;
    37  writeln(f[n]);
    38  end;
    39 begin
    40  main;
    41 end.     
    View Code

    需要注意一点:将二维转化为一维后,内循环变成了倒序,而二维的话是不用考虑这个的(自己想想为什么)

  • 相关阅读:
    Codeforces Round #419 (Div. 2)
    论蒟蒻的自我修养
    12 day 1
    Balanced Teams (USACO Jan Bronze 2014)
    一个奇怪的绘图程序
    BZOJ 1002 [ FJOI 2007 ]
    BZOJ 3540 realtime-update 解题
    准备做的题目
    代码风格与树形DP
    CH round #55 Streaming #6
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3812779.html
Copyright © 2011-2022 走看看