[关闭]
@y20070316 2017-03-15T10:45:23.000000Z 字数 2262 阅读 1821

【xsy2166】Hope cdq分治+NTT

题目 分治 FFT


题意

  对于 的一个排列 ,我们定义这个序列的P值和Q值;
  对于每个 ,如果存在一个最小的 ,使得 ,则将点 与点 连一条无向边。于是就得到了一幅图。计算这个图的连通块的大小,将它们相乘,得到 。记
  求 的所有排列的Q值之和。

  

分析

  设 表示 的所有排列的Q值之和,考虑如何求
  我们用递推的方式求 。枚举 所在的位置为 ,则前 个选择的方案数有 个。贡献和为 。综上,得到



  考虑使用分治+NTT解决。

实现

  在这道题中,我学会了一些NTT的新姿势——这样写常数小很多。
  姿势1: 的简易交换。
F(i,0,n-1) rev[i]=rev[i>>1]>>1|(i&1)<<(bit-1)
  姿势2: wi[i],wiv[i] 表示
  姿势3:空间开大4倍(因为 wiwiv )。
  更多的细节见代码。

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <cctype>
  4. using namespace std;
  5. #define F(i,a,b) for (int i=(a);i<=(b);i++)
  6. #define LL long long
  7. const int N=524288;
  8. const int L=998244353;
  9. const int G=3;
  10. int n,k;
  11. int b[N];
  12. int inv[N];
  13. int wi[N],wiv[N];
  14. int rev[N];
  15. int a[N];
  16. int ans;
  17. int pwr(int x,int y) {
  18. int mul=0;
  19. for (;y>0;y>>=1,x=(LL)x*x%L)
  20. if (y&1) mul=(LL)mul*x%L;
  21. return mul;
  22. }
  23. void NTT(int *a,int n,int bit,int kd) {
  24. F(i,0,n-1) if (i<rev[i]) swap(a[i],a[rev[i]]);
  25. for (int i=2;i<=n;i<<=1) {
  26. int wn=(kd==1?wi[i]:wiv[i]);
  27. for (int j=0;j<n;j+=i) {
  28. int w=1;
  29. F(k,0,i/2-1) {
  30. int x=a[j+k],y=(LL)w*a[j+k+i/2]%L;
  31. a[j+k]=(x+y)%L,a[j+k+i/2]=((x-y)%L+L)%L;
  32. w=(LL)w*wn%L;
  33. }
  34. }
  35. }
  36. if (kd==-1) {
  37. int invn=pwr(n,L-2);
  38. F(i,0,n-1) a[i]=(LL)a[i]*invn%L;
  39. }
  40. }
  41. void Solve(int l,int r) {
  42. if (l==r) {
  43. if (!l) a[l]=1; else a[l]=(LL)a[l]*inv[l]%L;
  44. return;
  45. }
  46. int mid=(l+r)>>1;
  47. Solve(l,mid);
  48. int n1=mid-l+1,d1=l;
  49. int n2=r-l,d2=1;
  50. int dall=d1+d2;
  51. int len=1,bit=0;
  52. while (len<=(n1-1)+(n2-1)) len<<=1,bit++;
  53. F(i,0,len-1) rev[i]=rev[i>>1]>>1|(i&1)<<(bit-1);
  54. static int ta[N],tb[N];
  55. F(i,0,len-1) ta[i]=tb[i]=0;
  56. F(i,d1,mid) ta[i-d1]=a[i];
  57. F(i,d2,r-l) tb[i-d2]=b[i];
  58. NTT(ta,len,bit,1);
  59. NTT(tb,len,bit,1);
  60. F(i,0,len-1)
  61. ta[i]=(LL)ta[i]*tb[i]%L;
  62. NTT(ta,len,bit,-1);
  63. F(i,mid+1,r)
  64. a[i]=(a[i]+ta[i-dall])%L;
  65. Solve(mid+1,r);
  66. }
  67. int main(void) {
  68. #ifndef ONLINE_JUDGE
  69. freopen("sdchr.in","r",stdin);
  70. freopen("sdchr.out","w",stdout);
  71. #endif
  72. cin>>n>>k;
  73. inv[1]=1; F(i,2,n) inv[i]=((LL)L-L/i)*inv[L%i]%L;
  74. F(i,0,n) b[i]=pwr(i,k);
  75. int bit=0,len=1;
  76. while (len<=n+n) bit++,len<<=1;
  77. int invG=pwr(G,L-2);
  78. F(i,0,bit) {
  79. wi[1<<i]=pwr(G,(L-1)/(1<<i));
  80. wiv[1<<i]=pwr(invG,(L-1)/(1<<i));
  81. }
  82. Solve(0,n);
  83. ans=a[n]; F(i,1,n) ans=(LL)ans*i%L; cout<<ans<<endl;
  84. return 0;
  85. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注