[关闭]
@ysner 2018-10-16T09:16:56.000000Z 字数 1661 阅读 1497

交换

DP 排列


题面

给定一个的排列。一个的排列被认为是优美
的排列,当且仅当满足下列条件:
对排列进行次交换。

最后能使得排列
问有多少个优美的排列,答案对取模。

解析

首先显而易见的是这个排列有对逆序对。

能影响每个位置的值的交换,只有两个:

然后想想把调到其在中的位置需要满足哪些条件。
,说明要向左调,因此其前面一个的交换应比其自己的交换靠前。
而在中,它们自己的交换应比其前面一个的交换靠前,否则无法保证到达

这样就能得出这个交换的相对顺序。
为统计到第个位置的交换,该交换在排列中排第个。
然后再枚举上一个位置交换排第几个,依限制转移即可,复杂度
发现可以转移的部分不是前缀就是后缀,可以前缀和优化,复杂度降到

嗯,还有个只能让蒟蒻挂掉的细节:
的转移是合法的。
因为从前往后转移的过程实际上是插入数的过程,所以如果第个位置的交换在第个,后面所有交换的位置都会后移一位。

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstdio>
  4. #include<cstdlib>
  5. #include<cstring>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define fp(i,a,b) for(re int i=a;i<=b;++i)
  11. #define fq(i,a,b) for(re int i=a;i>=b;--i)
  12. using namespace std;
  13. const int N=5e3+100,mod=1e9+7;
  14. int n,pos[N],ans,op[N],tag=1,f[N][N],g[N][N];
  15. bool vis[N];
  16. il ll gi()
  17. {
  18. re ll x=0,t=1;
  19. re char ch=getchar();
  20. while(ch!=-'-'&&(ch<'0'||ch>'9')) ch=getchar();
  21. if(ch=='-') t=-1,ch=getchar();
  22. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  23. return x*t;
  24. }
  25. //op前面操作先于后面操作1 or 前面操作后于后面操作2
  26. il void Pre()
  27. {
  28. fp(i,1,n)
  29. {
  30. if(pos[i]==i) tag=0;
  31. if(pos[i]<i)
  32. {
  33. if(pos[i]!=1) op[i]|=1;
  34. fp(j,pos[i]+1,i-1) op[j]|=2;
  35. }
  36. if(pos[i]>i)
  37. {
  38. if(pos[i]!=n) op[i]|=2;
  39. fp(j,i+1,pos[i]-1) op[j]|=1;
  40. }
  41. }
  42. fp(i,1,n-1) if(op[i]==3) tag=0;
  43. }
  44. int main()
  45. {
  46. n=gi();
  47. fp(i,1,n) pos[gi()+1]=i;
  48. Pre();
  49. if(!tag) return puts("0"),0;
  50. f[1][1]=g[1][1]=1;
  51. fp(i,2,n-1)
  52. {
  53. fp(j,1,i)
  54. {
  55. if(op[i]==0) (f[i][j]+=g[i-1][i-1])%=mod;
  56. if(op[i]==1) (f[i][j]+=g[i-1][j-1])%=mod;
  57. if(op[i]==2) (f[i][j]+=g[i-1][i-1]-g[i-1][j-1]+mod)%=mod;
  58. }
  59. fp(j,1,i) g[i][j]=(g[i][j-1]+f[i][j])%mod;
  60. }
  61. printf("%d\n",g[n-1][n-1]);
  62. return 0;
  63. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注