1. 程式人生 > >luogu P4161 [SCOI2009]遊戲

luogu P4161 [SCOI2009]遊戲

依次 math 轉化 its 轉移 getch arr 整數 print

傳送門

我們發現整個大置換中,會由若幹形如\((a_1\rightarrow a_2,a_2\rightarrow a_3,...a_{n-1}\rightarrow a_n,a_n\rightarrow a_1)\)的循環置換組成,記某個循環置換中元素個數為\(m_i\)而整個置換的循環節大小為\(lcm(m_1,m_2,...)\),那麽問題轉化成把一個數\(n\)拆成若幹整數之和,問拆出來的整數的\(lcm\)有多少種

\([1,n]\)的質數篩出來,然後dfs,從前往後考慮質數\(p_i\),每次從剩余的數中減去\({p_i}^k\),假設某個時刻表示的數為\(s\),那麽減去\({p_i}^k\)

後就能表示\(s*{p_i}^k\),這樣子計算是不重不漏的,但是無法通過此題(方案數為\(long\ long\)級別)

考慮dp,設\(f_i\)\(n=i\)時的答案,然後依次枚舉質數,因為當前考慮的質數之前沒考慮,所以\(f_i\)可以從\(f_{i-p_j},f_{i-{p_j}^2},f_{i-{p_j}^3}...\)轉移過來,這其實就是個背包

詳見代碼

#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define eps (1e-5)

using namespace std;
il LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int prm[200],tt,n;
char vis[1010];
il void init()
{
  for(int i=2;i<=n;i++)
    {
      if(!vis[i]) prm[++tt]=i;
      for(int j=1;j<=tt&&i*prm[j]<=n;j++)
        {
          vis[i*prm[j]]=true;
          if(i%prm[j]==0) break;
        }
    }
}
LL f[1010];
/*void dfs(int o,int s)
{
  if(o>tt||s<prm[o]) return;
  dfs(o+1,s);
  int xx=prm[o];
  while(s>=xx)
    {
      ++ans;
      dfs(o+1,s-xx);
      xx*=prm[o];
    }
}*/

int main()
{
  n=rd();
  init();
  for(int i=0;i<=n;i++) f[i]=1;
  for(int i=1;i<=tt;i++)
    for(int j=n;j>=0;j--)
      for(int k=prm[i];j-k>=0;k*=prm[i])
        f[j]+=f[j-k];
  printf("%lld\n",f[n]);
  return 0;
}

luogu P4161 [SCOI2009]遊戲