SCOI2009 遊戲
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想知道,對於所有可能的對應關系,有多少種可能的排數。
Hint:
n<=1000
Solution:
所謂的對應關系就是置換。
開始想到置換群,發現不能直接轉化。
思考為什麽會重復回來,因為,向每一個置換的位置連一條有向邊,
n個點n條邊,並且每個點必然一個入度一個出度,所以一定連出來若幹個環。
發現,環長就是這個環上的點重復一次的排數
所以,排數就是lcm(L1,L2,L3,L4...Lk)+1了。
而由於∑Li=n,所以,所有的L其實就是n的一個拆分。
那麽答案就是,n的所有正整數拆分中,拆分的所有數的不同的lcm有幾個。
但是n的拆分太多太多了。。。這種算法也就算到n=50就不行了。
無法繼續考慮。
正難則反。
考慮湊出多少個不同的lcm不行的話,
就欽定一個數m是lcm,看能不能拼出來。
m顯然無法枚舉。那就考慮m作為lcm是怎麽來的。
我們把m質因數分解一下,
m=p1^q1*p2^q2*...pk^qk
那麽,由於lcm是所有的數中質因數分解的所有質數指數取一個max,
這些原來的拆分的數中,分別質因數分解,質數指數的max就分別是:q1,q2...qk
所以,只要判斷p1^q1+p2^q2+...+pk^qk<=n是否成立即可。
若成立,那麽m就是一個合法可以拼湊的lcm
(相當於,我們只取這些質數的最大指數,作和,剩下的全部取1。
反正對於lcm=m來講,取其他的數,也都是被max埋沒了。)
我們的問題就轉化成:p1^a1+p2^a2+..+pl^al<=n的解的個數。(對於每一個不同的a的選擇,就對應一種lcm)
(其中,pl就是最大的小於n的質數)
然後就是多重背包啦!
物品上限,只要pk^ak不要超過n即可,基本沒有幾件。
復雜度:O(N^2)左右
代碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1000+5; int pri[N],tot; ll f[N]; bool vis[N]; int n; void sieve(){ for(int i=2;i<=n;i++){ if(!vis[i]){ pri[++tot]=i; } for(int j=1;j<=tot;j++){ if(pri[j]*i>n) break; vis[pri[j]*i]=1; if(i%pri[j]==0) break; } } } int main() { scanf("%d",&n); sieve(); f[0]=1; for(int i=1;i<=tot;i++) for(int j=n;j>=0;j--){ for(int k=pri[i];k<=j;k*=pri[i]){ f[j]+=f[j-k]; } } ll ans=0; for(int i=0;i<=n;i++)ans+=f[i]; printf("%lld",ans); return 0; }
總結:
還是正難則反沒有想到啊。之前都想到了。
卡在了自然數拆分的方案怎麽統計。。。
有時候,直接求拼湊的方案等無法處理,
就考慮反著來,這個方案能否被拼湊。
二分答案本質就是這個思路。
相當於,我不知道答案求答案,和我猜一個答案驗證一下。
SCOI2009 遊戲