ABC226F x ABC180F
显然答案为 \(LCM(l_1,l_2,l_3...l_k)\),其中 \(l\) 是置换环长度。
考虑 \(DP\) 处理 \(LCM\) 。
相信大家看得懂英文。这里只解释一下 \(\dbinom{n-i-1}{x-1}\)。意义是先选一个固定的起点,这个起点编号最小,再选剩下的点。 为什么要起点编号最小?试想一下,我们选多次时,假设有四个点,不固定起点,每次选两个:
这个图有点鬼畜。。。蓝色表示第一次选的,红色为第二次选的。另一种情况是:
这难道是传说中的小红帽?但实际上这两种情况是一样的。于是我们必须强制性的每轮先选编号最小的。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50,P=998244353;
map<ll,ll> dp[N+5];
int n,k,fac[N+5],inv[N+5],ans;
int KSM(int a,int b)
{
int ret=1;
while(b)
{
if(b&1) ret=1ll*ret*a%P;
a=1ll*a*a%P;b>>=1;
}
return ret;
}
int C(int a,int b) {return 1ll*fac[a]*inv[b]%P*inv[a-b]%P;}
int lcm(int a,int b){return a/__gcd(a,b)*b;}
int main()
{
fac[0]=inv[0]=1;for(int i=1; i<=N; i++) fac[i]=1ll*fac[i-1]*i%P;
inv[N]=KSM(fac[N],P-2);
for(int i=N-1; i>=1; i--) inv[i]=1ll*(i+1)*inv[i+1]%P;
cin>>n>>k;
dp[0][1]=1;
for(int i=0; i<n; i++)
for(int j=1; i+j<=n; j++)
for(auto p:dp[i])
dp[i+j][lcm(p.first,j)]+=p.second*fac[j-1]%P*C(n-i-1,j-1)%P,
dp[i+j][lcm(p.first,j)]%=P;
for(auto p:dp[n]) ans=(ans+1ll*p.second*KSM(p.first,k)%P)%P;cout<<ans;
return 0;
}
这题只是顺带一提,因为也用到了这个结论。
题解就自己看吧。。也是用 \(DP\) 。
code
#include<bits/stdc++.h>
using namespace std;
const int N=300,P=1e9+7,inv2=(P+1)/2;
int n,m,L,fac[N+5],inv[N+5];
int dp[N+5][N+5];
int KSM(int a,int b)
{
int ret=1;
while(b)
{
if(b&1) ret=1ll*ret*a%P;
a=1ll*a*a%P;b>>=1;
}
return ret;
}
int C(int a,int b) {return 1ll*fac[a]*inv[b]%P*inv[a-b]%P;}
void upd(int &x,int y) {x=(x+y%P)%P;x=(x+P)%P;}
int solve(int lim)
{
memset(dp,0,sizeof dp);
dp[0][0]=1;
for(int i=0; i<=n; i++)
for(int j=0; j<=m; j++)
{
if(lim>0&&i+1<=n) upd(dp[i+1][j],dp[i][j]);
if(lim>1&&i+2<=n&&j+2<=m) upd(dp[i+2][j+2],1ll*dp[i][j]*(n-i-1)%P);
for(int k=2; k<=min(lim,n); k++)
{
if(i+k<=n&&j+k-1<=m) upd(dp[i+k][j+k-1],1ll*dp[i][j]*C(n-i-1,k-1)%P*fac[k]%P*inv2%P);
if(i+k<=n&&j+k<=m&&k>2) upd(dp[i+k][j+k],1ll*dp[i][j]*C(n-i-1,k-1)%P*fac[k-1]%P*inv2%P);
}
}
return dp[n][m];
}
int main()
{
fac[0]=inv[0]=1;for(int i=1; i<=N; i++) fac[i]=1ll*fac[i-1]*i%P;
inv[N]=KSM(fac[N],P-2);
for(int i=N-1; i>=1; i--) inv[i]=1ll*(i+1)*inv[i+1]%P;
scanf("%d%d%d",&n,&m,&L);
printf("%d",(solve(L)-solve(L-1)+P)%P);
return 0;
}
The End.