Description(P4161)
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想知道,對於所有可能的對應關係,有多少種可能的排數。
Description(P6280)
Farmer John(又)想到了一個新的奶牛晨練方案!
如同之前,Farmer John 的 $N$ 頭奶牛站成一排。對於$1\leq i \leq N$的每一個 $i$,從左往右第 $i$ 頭奶牛的編號為 $i$。他告訴她們重複以下步驟,直到奶牛們與她們開始時的順序相同。
給定長為 $N$ 的一個排列 $A$,奶牛們改變她們的順序,使得在改變之前從左往右第 $i$ 頭奶牛在改變之後為從左往右第 $A_i$ 頭。
例如,如果 $A=(1,2,3,4,5)$,那麼奶牛們總共進行一步。如果 $A=(2,3,1,5,4)$,那麼奶牛們總共進行六步。每步之後奶牛們從左往右的順序如下:
0 步:$(1,2,3,4,5)$
1 步:$(3,1,2,5,4)$
2 步:$(2,3,1,4,5)$
3 步:$(1,2,3,5,4)$
4 步:$(3,1,2,4,5)$
5 步:$(2,3,1,5,4)$
6 步:$(1,2,3,4,5)$
求所有正整數 $K$ 的和,使得存在一個長為 $N$ 的排列,奶牛們需要進行恰好 $K$ 步。
由於這個數字可能非常大,輸出答案模 $M$ 的餘數($10^8 \leq M \leq 10^9+7$,$M$ 是質數)。
Solution
兩個問題,一個解法
兩題的題目表述都表明題中給出的數的對應關係形成了一張圖,這張圖由多個環組成,可能有自環,環長之和為$N$
最終的答案分別是每個環長的$lcm$個數/和
對於一列數的$lcm$,等於這列數質因數分解後每個質數對應的最高次之積
所以現在要求將$N$分解,為了使分解後的數能組成更多種$lcm$,最優的選擇應是讓這列數互質
因為有可能分解出$1$,所以對於分解出$1$的個數不同,依次拆分$[1,N]$中所有數,輸出它們的答案之和
考慮列舉質數
設$f_{i,j}$表示在前$i$個質數中,拆分數$j$所能得到的$lcm$個數/和
對於P4161:
$$f_{i,j}=\sum_{k=0}^{j\geq p_{i}^k} dp_{i-1,j-p_{i}^k}$$
對於P6280:
$$f_{i,j}=\sum_{k=0}^{j\geq p_{i}^k} dp_{i-1,j-p_{i}^k}\times p_{i}^k$$
#include<iostream> #include<cstdio> using namespace std; int n,tot,prime[1005]; long long dp[1005],ans; bool vst[1005]; inline int read() { int w=0,f=1; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return w*f; } int main() { n=read(); for(int i=2;i<=n;i++) { if(!vst[i]) { prime[++tot]=i; } for(int j=1;j<=tot&&prime[j]*i<=n;j++) { vst[prime[j]*i]=true; if(!(i%prime[j])) { break; } } } dp[0]=1; for(int i=1;i<=tot;i++) { for(int j=n;j>=prime[i];j--) { int temp=prime[i]; while(j>=temp) { dp[j]+=dp[j-temp]; temp*=prime[i]; } } } for(int i=1;i<=n;i++) { ans+=dp[i]; } printf("%lld\n",ans+1); return 0; }
#include<iostream> #include<cstdio> using namespace std; long long n,tot,prime[10005],mod; long long dp[10005],ans; bool vst[10005]; inline long long read() { long long w=0,f=1; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return w*f; } int main() { n=read(); mod=read(); for(long long i=2;i<=n;i++) { if(!vst[i]) { prime[++tot]=i; } for(long long j=1;j<=tot&&prime[j]*i<=n;j++) { vst[prime[j]*i]=true; if(!(i%prime[j])) { break; } } } dp[0]=1; for(long long i=1;i<=tot;i++) { for(long long j=n;j>=prime[i];j--) { long long temp=prime[i]; while(j>=temp) { (dp[j]+=dp[j-temp]*temp)%=mod; temp*=prime[i]; } } } for(long long i=1;i<=n;i++) { (ans+=dp[i])%mod; } printf("%lld\n",(ans+1)%mod); return 0; }