题解:P10547 [THUPC2024] 排列游戏
Union_of_Britain · · 题解
先考察操作的性质。
注意到若设每个点的势能是
这个形式看起来就很正确。猜想其可以取到下界,所以有:
引理 1:一个排列的最小交换代价是
证明:
只需说明对于每个非恒等的排列有一个使总势能减少
取
再考虑“交换
引理 2:可以交换
这是容易证明的:奇偶性不等于
奇排列和偶排列的答案应该不会差太远,并且应该具有某种模式。打表不难发现:
引理 3:对于
证明(不过显然考试时这个结论是没有必要证明的):考虑建立一个使得势能和不变的奇偶排列的映射。如果存在一个使势能和不变的交换就交换一次,这样的映射显然可逆,这样只需考虑那些不能交换的。
那些不能交换的就是循环都由连续数字构成的排列。设有
这样问题就被几乎转化为 ABC134F。然后我们队在做的时候看到数据范围就不知道怎么做了。
事实上这里的 DP 就是采取 ABC134F 的方法。比如这一篇题解。但是那道题的时间复杂度是
但是本题具有更特殊的性质:
这样最后的复杂度就是
注:代码里的状态实际上是官方题解的状态,也就是说这个代码的
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int T,n,m,N=500,M=10000,SM=80;
int f[2][160][10005],fac[100005],ifac[10005];
int qp(int a,int b){
int res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
inline int C(int a,int b){
if(b>a)return 0;
return fac[a]*ifac[b]%mod*ifac[a-b]%mod;
}
int s[505][20005];
inline int pow1(int x){
if(x&1)return mod-1;
return 1;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
fac[0]=1;
for(int i=1;i<=M;i++)fac[i]=fac[i-1]*i%mod;
ifac[M]=qp(fac[M],mod-2);
for(int i=M-1;i>=0;i--)ifac[i]=ifac[i+1]*(i+1)%mod;
int I2=(mod+1)/2,cur=0;
f[cur][0][0]=1;
for(int i=1;i<=N;i++){
cur^=1;
for(int d=0;d<=min(SM,i);d++){
for(int k=(d-1)*d;k<=M;k+=2){
f[cur][d][k]=0;
if(d>0)f[cur][d][k]=f[cur^1][d-1][k-2*(d-1)];
if(k>=2*d)f[cur][d][k]+=f[cur^1][d][k-2*d]*(2*d+1);
if(f[cur][d][k]>=mod)f[cur][d][k]-=mod;
if(k>=2*(d+1))(f[cur][d][k]+=f[cur^1][d+1][k-2*(d+1)]*(d+1)*(d+1))%=mod;
}
}
for(int j=0;j<=M;j++){
if(j&1)continue;
int k=j/2,F=f[cur][0][j];
if(k<=i)(F+=pow1((i&1)+(k&1))*C(i-1,k)%mod)%=mod;
F=F*I2%mod;
s[i][j]=((j>0?s[i][j-2]:0)+F)%mod;
}
}
cin>>T;
while(T--){
cin>>n>>m;
int ans=s[n][m*2];
cout<<ans<<endl;
}
return 0;
}