P8859 冒泡排序 题解
type=1
一个显然的贪心策略是我们从小到大检查每个
接下来考虑什么时候会省下一次操作,也就是
移动
于是
dp,记
这个转移其实就是第一类斯特林数,一种比较直观的理解是把前缀最大值加上他后面的这一段区间看成一个圆排列,前缀最大值是这个圆排列的代表元,容易证明这是一个双射。
type=2
顺着上一问的思路继续,考虑
第一次操作无法经过整个环,且后续操作不会经过
先考虑给定一个排列怎样求
注意到有且仅有
这样连边形成的结构是一个内向树,图中的最长路等价于将有向边视为无向边后,以
容易证明对于所有满足父亲节点编号小于儿子节点编号的生成树和
还是 dp,记
直接转移是
#include<iostream>
#include<cstdio>
using namespace std;
const int mod=1e9+7;
int n,m,t,f[501][501],g[501][501],sum[501][501],c[501][501];
inline int Mod(int x)
{
return x>=mod? x-mod:x;
}
int main()
{
cin>>n>>m>>t;
if(t==1)
{
f[1][0]=1;
for(int i=2;i<=n;++i)
{
f[i][0]=1;
for(int j=1;j<i;++j)
f[i][j]=Mod(f[i-1][j]+1ll*(i-1)*f[i-1][j-1]%mod);
}
cout<<f[n][m]<<'\n';
}
else
{
c[0][0]=1;
for(int i=1;i<=n;++i)
{
c[i][0]=1;
for(int j=1;j<=n;++j)
c[i][j]=Mod(c[i-1][j]+c[i-1][j-1]);
}
g[1][1]=1;
for(int i=1;i<=n;++i)
sum[1][i]=1;
for(int i=2;i<=n;++i)
for(int j=2;j<=n;++j)
{
if(j<=i)
for(int k=1;k<i;++k)
g[i][j]=Mod(g[i][j]+1ll*c[i-2][k-1]*Mod(1ll*g[k][j]*sum[i-k][j-1]%mod+1ll*sum[k][j-1]*g[i-k][j-1]%mod)%mod);
sum[i][j]=Mod(sum[i][j-1]+g[i][j]);
}
cout<<g[n][n-m]<<'\n';
}
return 0;
}