1 条题解
-
0
自动搬运
来自洛谷,原作者为

Union_of_Britain
神于天搬运于
2025-08-24 22:59:04,当前版本为作者最后更新于2024-06-19 19:24:25,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
先考察操作的性质。
注意到若设每个点的势能是 ,一次代价为 的操作的最多使得总势能减少 。因此有不等式:
这个形式看起来就很正确。猜想其可以取到下界,所以有:
引理 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; }
- 1
信息
- ID
- 10305
- 时间
- 2500ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者