一、题目描述:
设 $\pi (x)$ 为全排列 $x$ 的逆序对数。
给定 $n,m$,求有多少对长度为 $n$ 的排列 $p,q$,使得 $p$ 的字典序小于 $q$,且 $\pi (p)>\pi (q)$
答案对 $m$ 取模。数据范围:$1\le n\le 500,1\le m\le 10^9$。
二、解题思路:
一开始列出计算式个人感觉是最难的一步,后面化简多半就是套路了。
因为字典序相较于逆序对数要简单一些,所以我们先考虑字典序。
枚举排列 $p$ 在第 $i$ 个位置第一次比 $q$ 小,枚举 $i$ 位置上 $p,q$ 的大小,再考虑逆序对数。
注意到前 $i-1$ 位不会对逆序对数之差造成任何影响,无论是内部还是对外都是这样。
对于后面 $n-i$ 位的部分显然应该计数 $dp$,因为没有其他思路,数据范围也提示你了。
设 $f_{i,j}$ 表示长度为 $i$ 的排列对 $(p,q)$ 满足 $\pi(p)-\pi(q)$ 的数量为 $j$ 的方案数。
为什么这样设状态呢?因为我们对于每一个长度的 $(p,q)$ 都要统计答案,而且这样很好转移。
显而易见的,$ans=\sum_{i=1}^n{n\choose n-i}\times (n-i)! \times (\sum_{j=1}^{lim}\sum_{p=1}^i\sum_{q=p+1}^if_{i-1,j-p+q})$
注意这里枚举的 $i$ 与前面意义不一样,这里的 $i$ 是枚举的后面一部分的长度。
显然我们需要做到对 $f$ 数组的 $O(1)$ 转移。先推一下式子:
$f_{i,j}=\sum_{p=1}^i\sum_{q=1}^if_{i-1,j-p+q}$
$=\sum_{p-q=1-i}^{i-1}f_{i-1,j-(p-q)}\times (i-\left| p-q\right|)$
$=\sum_{\mid d \mid =0}^{i-1}f_{i-1,j-d}\times (i-\left|d\right|)$
$=\sum_{d=1}^if_{i-1,j+d}\times (i-d)+\sum_{d=0}^{i-1}f_{i-1,j-d}\times (i-d)$
$=S1_{i,j}+S2_{i,j}$($S1_{i,j},S2_{i,j}$ 分别代替上个式子中的两个部分)
$S1_{i,j}=\sum_{d=1}^{i}f_{i-1,j+d}\times i-\sum_{d=1}^{i}f_{i-1,j+d}\times d$
$=\sum_{d=1}^{i}f_{i-1,j+d}\times (i+j)-\sum_{d=1}^{i}f_{i-1,j+d}\times (j+d)$
$S2_{i,j}=\sum_{d=0}^{i-1}f_{i-1,j-d}\times i-\sum_{d=0}^{i-1}f_{i-1,j-d}\times d$
$=\sum_{d=0}^{i-1}f_{i-1,j-d}\times (i-j)+\sum_{d=0}^{i-1}f_{i-1,j-d}\times (j-d)$
这里的转化比较有意思,将系数化来与枚举的系数相同(如 $j+d,j-d$),便于前缀和优化。
令 $G1_{i,j}=\sum_{k=-lim}^jf_{i,k},G2_{i,j}=\sum_{k=-lim}^jf_{i,k}\times k$,那么有:
$S1_{i,j}=(i+j)\times (G1_{i-1,i+j}-G1_{i-1,j})+G2_{i-1,j}-G2_{i-1,j+i}$
$S2_{i,j}=(i-j)\times (G1_{i-1,j}-G1_{i-1,j-i})+G2_{i-1,j}-G2_{i-1,j-i}$
至此,我们可以 $O(1)$ 转移方程,总时间复杂度 $O(n^3)$。
还有一点需要注意的就是 $f_{i,j}$ 中的 $j$ 可以是负数,注意数组下标加上一个定值来储存。代码中用 $M$ 来表示。
三、完整代码:
1 #include<iostream> 2 3 #define N 510 4 #define M 130000 5 #define ll long long 6 #define rep(i,l,r) for(ll i=l;i<=r;i++) 7 8 using namespace std; 9 10 ll n,mod,ans,fac[N],C[N][N]; 11 ll f[M<<1],s1[M<<1],s2[M<<1],g1[M<<1],g2[M<<1]; 12 13 void calc(ll x){ 14 15 ll lim=x*(x-1)/2,cnt=0; 16 17 rep(i,M-lim-x,M+lim+x){ 18 g1[i]=(g1[i-1]+f[i])%mod; 19 g2[i]=(g2[i-1]+f[i]*i)%mod; 20 } 21 22 rep(i,M-lim,M+lim){ 23 s1[i]=(x+i)*(g1[i+x]-g1[i])+g2[i]-g2[i+x]; 24 s2[i]=(x-i)*(g1[i]-g1[i-x])+g2[i]-g2[i-x]; 25 } 26 27 rep(i,M-lim,M+lim){ 28 s1[i]=(s1[i]%mod+mod)%mod; 29 s2[i]=(s2[i]%mod+mod)%mod; 30 } 31 32 rep(i,M+1,M+lim) (cnt+=s1[i])%=mod; 33 (ans+=C[n][n-x]*fac[n-x]%mod*cnt%mod)%=mod; 34 35 rep(i,M-lim,M+lim) f[i]=(s1[i]+s2[i])%mod; 36 37 } 38 39 signed main(){ 40 41 ios::sync_with_stdio(false); 42 cin.tie(0); cout.tie(0); 43 44 cin>>n>>mod; fac[0]=f[M]=1; 45 rep(i,1,n) fac[i]=fac[i-1]*i%mod; 46 47 rep(i,0,n) C[i][0]=1; 48 rep(i,0,n) rep(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; 49 50 rep(i,2,n) calc(i); 51 cout<<ans<<'\n'; 52 53 return 0; 54 55 }
四、写题心得:
感觉 $feecle$ 选的题都还挺有趣,拜谢 。
并且掌握了新的推式子技巧和方法,很高兴。
忘了说,顺便水了一道蓝题,$CF1542E1$。
标签:G2,G1,题解,rep,times,CF1542E2,pi,sum From: https://www.cnblogs.com/yhy-trh/p/18004098/CF1542E2