P10547 [THUPC 2024 决赛] 排列游戏 题解
Coffee_zzz · · 题解
设格子
性质:排列
p 被还原所需时间为\dfrac 1 2\sum\limits_{i=1}^n |p_i-i| 。证明:显然
\dfrac 1 2\sum\limits_{i=1}^n |p_i-i| 是下界,而任意时刻一定存在(i,j) 满足交换i,j 带来的贡献是满的,因此\dfrac 1 2\sum\limits_{i=1}^n |p_i-i| 可以取到。
于是,能够用不超过
性质:
\sum\limits_{i=1}^n |p_i-i|=2\sum\limits_{i=1}^n [p_i \gt i](p_i-i) 证明:显然一个环中上升的边的长度之和与下降的边的长度之和相等,即
\sum\limits_{i=1}^n [p_i \gt i](p_i-i)=\sum\limits_{i=1}^n [p_i \lt i](p_i-i) ,而左式与右式之和等于\sum\limits_{i=1}^n |p_i-i| ,因此原式成立。
于是,
性质:排列
p 是通过n 次交换操作生成的,等价于排列p 的置换环数量为偶数。证明:初始时排列
p 的置换环数量的奇偶性与n 的奇偶性相同,而每次交换操作都会使排列p 的置换环数量改变1 ,因此n 次交换操作结束后排列p 的置换环数量为偶数。
于是问题变成了,对
考虑把每一对
初始化
性质:第二维是
\mathcal O(\sqrt m) 的。证明:如果第二维的值超过了
\sqrt m ,就算每次都合并一条链,均摊下来每条链也会造成至少\sqrt m 的贡献,导致总贡献超过m 。
于是总时间复杂度为
const int N=505,M=5005,S=72,mod=1e9+7;
int T,n,m,f[2][S][M][2],ans[M];
vector <pii> ve[N];
void add(int &a,int b){
a+=b;
if(a>=mod) a-=mod;
}
void solve(){
cin>>T;
for(int i=1;i<=T;i++) cin>>n>>m,ve[n].pb({m,i});
n=500,m=5000,f[0][0][0][0]=1;
for(int i=1;i<=n;i++){
int ii=i&1;
memset(f[ii],0,sizeof f[ii]);
for(int j=0;j<min(i,S);j++){
for(int k=0,kk=j;kk<=m;k++,kk++){
for(int x=0;x<2;x++){
if(!f[ii^1][j][k][x]) continue;
if(j+1<S) add(f[ii][j+1][kk][x],f[ii^1][j][k][x]);
add(f[ii][j][kk][1-x],f[ii^1][j][k][x]);
add(f[ii][j][kk][x],2ll*j*f[ii^1][j][k][x]%mod);
if(j) add(f[ii][j-1][kk][x],1ll*j*(j-1)*f[ii^1][j][k][x]%mod);
if(j) add(f[ii][j-1][kk][1-x],1ll*j*f[ii^1][j][k][x]%mod);
}
}
}
for(auto p:ve[i]) for(int k=0;k<=p.fi;k++) add(ans[p.se],f[ii][0][k][0]);
}
for(int i=1;i<=T;i++) cout<<ans[i]<<endl;
}