1. 程式人生 > >SCOI2009 遊戲

SCOI2009 遊戲

復雜 答案 算法 和我 二分 const 本質 多重 排列

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 遊戲