随笔 - 58,  文章 - 0,  评论 - 4,  阅读 - 3296

一、题目描述

  设 π(x) 为全排列 x 的逆序对数。

  给定 n,m,求有多少对长度为 n 的排列 p,q,使得 p 的字典序小于 q,且 π(p)>π(q)

  答案对 m 取模。数据范围:1n500,1m109


 二、解题思路:

  一开始列出计算式个人感觉是最难的一步,后面化简多半就是套路了。

  因为字典序相较于逆序对数要简单一些,所以我们先考虑字典序。

  枚举排列 p 在第 i 个位置第一次比 q 小,枚举 i 位置上 p,q 的大小,再考虑逆序对数。

  注意到前 i1 位不会对逆序对数之差造成任何影响,无论是内部还是对外都是这样。

  对于后面 ni 位的部分显然应该计数 dp,因为没有其他思路,数据范围也提示你了。

  设 fi,j 表示长度为 i 的排列对 (p,q) 满足 π(p)π(q) 的数量为 j 的方案数。

   为什么这样设状态呢?因为我们对于每一个长度的 (p,q) 都要统计答案,而且这样很好转移。

  显而易见的,ans=i=1n(nni)×(ni)!×(j=1limp=1iq=p+1ifi1,jp+q)

  注意这里枚举的 i 与前面意义不一样,这里的 i 是枚举的后面一部分的长度。

  显然我们需要做到对 f 数组的 O(1) 转移。先推一下式子:

  fi,j=p=1iq=1ifi1,jp+q

  =pq=1ii1fi1,j(pq)×(i|pq|)

  =d∣=0i1fi1,jd×(i|d|)

  =d=1ifi1,j+d×(id)+d=0i1fi1,jd×(id)

  =S1i,j+S2i,jS1i,j,S2i,j 分别代替上个式子中的两个部分)

  S1i,j=d=1ifi1,j+d×id=1ifi1,j+d×d

  =d=1ifi1,j+d×(i+j)d=1ifi1,j+d×(j+d)

  S2i,j=d=0i1fi1,jd×id=0i1fi1,jd×d

  =d=0i1fi1,jd×(ij)+d=0i1fi1,jd×(jd)

  这里的转化比较有意思,将系数化来与枚举的系数相同(如 j+d,jd),便于前缀和优化。

  令 G1i,j=k=limjfi,kG2i,j=k=limjfi,k×k,那么有:

  S1i,j=(i+j)×(G1i1,i+jG1i1,j)+G2i1,jG2i1,j+i

  S2i,j=(ij)×(G1i1,jG1i1,ji)+G2i1,jG2i1,ji

  至此,我们可以 O(1) 转移方程,总时间复杂度 O(n3)

  还有一点需要注意的就是 fi,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

posted on   trh0630  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示