$\newcommand{\align}[1]{\begin{align*}#1\end{align*}}$题意:对于一个排列$p_{1\cdots n}$构造一个图,如果$i\lt j$且$p_i\lt p_j$,就连双向边$(i,j)$,这个排列的权值就是所有连通块的大小的乘积,求所有$n!$个排列的权值总和

考虑DP,设$f_n$表示(长度为$n$的排列构成的图)连通的方案数,则我们可以用$n!$减去不连通的方案数,考虑把不合法的方案按第一个连通块大小$i$分类,则后$n-i$个数可以随意乱排,所以$\align{f_n=n!-\sum\limits_{i=1}^{n-1}(n-i)!f_i}$

设答案为$g_n$,枚举最后一个连通块的大小$i$,我们得到转移$\align{g_n=\sum\limits_{i=1}^nif_ig_{n-i}}$

这样做是$O(n^2)$的,考虑优化

先转化一下$f$的递推式,$\align{2n!=[n=0]+\sum\limits_{i=0}^n(n-i)!f_i}$

设$\align{A(x)=\sum\limits_ii!x^i,B(x)=\sum\limits_if_ix^i}$,则$2A(x)=A(x)B(x)+1$,即$B(x)=\dfrac{2A(x)-1}{A(x)}$

直接多项式求逆可以算出$B(x)$,设$\align{C(x)=\sum\limits_iif_ix^i}$,现在考虑算答案,枚举答案中连通块的个数$k$,答案为$\align{[x^n]\sum\limits_{i=1}^\infty C^k(x)=[x^n]\left(\dfrac1{1-C(x)}-1\right)}$,算出$C(x)$再求逆就可以了

但是毒瘤yww把它出到$n\leq5\times10^5$,这样做如果写的丑会T掉,所以我们来卡卡常==

首先注意到$C(x)$其实就是$B(x)$的每一项系数多了一个$i$,所以$C(x)=xB'(x)$,再利用$B(x)=\dfrac{2A(x)-1}{A(x)}$,我们得到$\dfrac1{1-C(x)}=\dfrac{A^2(x)}{A^2(x)-xA'(x)}$,优化到了只用两次FFT和一次多项式求逆,已经可以过了

还可以继续优化,我们需要找到快速计算$\align{h_n=[x^n]A^2(x)=\sum\limits_{i=0}^ni!(n-i)!}$的方法,在OEIS上可以找到递推公式$h_n=n!+\dfrac{n+1}2h_{n-1}$,但我并没有找到证明,看看以后有没有机会填坑,这样做就可以只做一次多项式求逆

  1. #include<stdio.h>
  2. #include<string.h>
  3. const int mod=998244353,maxn=1048576;
  4. typedef long long ll;
  5. int mul(int a,int b){return a*(ll)b%mod;}
  6. int ad(int a,int b){return(a+b)%mod;}
  7. int de(int a,int b){return(a-b)%mod;}
  8. int pow(int a,int b){
  9. int s=1;
  10. while(b){
  11. if(b&1)s=mul(s,a);
  12. a=mul(a,a);
  13. b>>=1;
  14. }
  15. return s;
  16. }
  17. int rev[maxn],N,iN;
  18. void pre(int n){
  19. int i,k;
  20. for(N=1,k=0;N<n;N<<=1)k++;
  21. for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
  22. iN=pow(N,mod-2);
  23. }
  24. void swap(int&a,int&b){a^=b^=a^=b;}
  25. void ntt(int*a,int on){
  26. int i,j,k,t,w,wn;
  27. for(i=0;i<N;i++){
  28. if(i<rev[i])swap(a[i],a[rev[i]]);
  29. }
  30. for(i=2;i<=N;i<<=1){
  31. wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
  32. for(j=0;j<N;j+=i){
  33. w=1;
  34. for(k=0;k<i>>1;k++){
  35. t=mul(w,a[i/2+j+k]);
  36. a[i/2+j+k]=de(a[j+k],t);
  37. a[j+k]=ad(a[j+k],t);
  38. w=mul(w,wn);
  39. }
  40. }
  41. }
  42. if(on==-1){
  43. for(i=0;i<N;i++)a[i]=mul(a[i],iN);
  44. }
  45. }
  46. int t[maxn];
  47. void getinv(int*a,int*b,int n){
  48. if(n==1){
  49. b[0]=pow(a[0],mod-2);
  50. return;
  51. }
  52. int i;
  53. getinv(a,b,n>>1);
  54. pre(n<<1);
  55. memset(t,0,N<<2);
  56. memcpy(t,a,n<<2);
  57. ntt(t,1);
  58. ntt(b,1);
  59. for(i=0;i<N;i++)b[i]=mul(b[i],2-mul(b[i],t[i]));
  60. ntt(b,-1);
  61. for(i=n;i<N;i++)b[i]=0;
  62. }
  63. int a[maxn],b[maxn],c[maxn];
  64. int main(){
  65. int n,k,i,ans;
  66. scanf("%d",&n);
  67. for(k=1;k<=n;k<<=1);
  68. a[0]=1;
  69. for(i=1;i<=n;i++){
  70. a[i]=mul(i,a[i-1]);
  71. b[i]=mul(i,a[i]);
  72. }
  73. pre(k<<1);
  74. ntt(a,1);
  75. for(i=0;i<N;i++)a[i]=mul(a[i],a[i]);
  76. ntt(a,-1);
  77. for(i=0;i<=n;i++)b[i]=de(a[i],b[i]);
  78. getinv(b,c,k);
  79. ans=0;
  80. for(i=0;i<=n;i++)ans=ad(ans,mul(a[i],c[n-i]));
  81. printf("%d",ad(ans,mod));
  82. }

[Contest20180426]校门外的树的更多相关文章

  1. P1047 校门外的树

    P1047 校门外的树 题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0 ...

  2. Vijos1448校门外的树 题解

    Vijos1448校门外的树 题解 描述: 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的…… 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现 ...

  3. OpenJudge计算概论-校门外的树

    /*======================================================================== 校门外的树 总时间限制: 1000ms 内存限制: ...

  4. [swustoj 764] 校门外的树 Plus Plus

    校门外的树 Plus Plus(0764) 问题描述 西南某科技大学的校门外长度为 L 的公路上有一排树,每两棵相邻的树之间的间隔都是 1 米.我们可以把马路看成一个数轴,马路的一端在数轴 1 的位置 ...

  5. 校门外的树 - Grids2808

    校门外的树 问题描述: 某校大门外长度为 L 的马路上有一排树,每两棵相邻的树之间的间隔都是1 米.我们 可以把马路看成一个数轴,马路的一端在数轴0 的位置,另一端在L 的位置:数轴上的每 个整数点, ...

  6. 校门外的树 OpenJudge 1.6.06

    06:校门外的树 总时间限制:  1000ms 内存限制:  65536kB 描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0 ...

  7. 【解题报告】VijosP1448校门外的树(困难版)

    原题: 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的--如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r ...

  8. Vijos P1103 校门外的树【线段树,模拟】

    校门外的树 描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0,1,2,……, ...

  9. Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】

    校门外的树 描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的…… 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: K=1,K= ...

随机推荐

  1. taotao用户登录springMVC拦截器的实现

    在springMVC中写拦截器,只需要两步: 一.写 java 拦截器类,实现 interceptor 拦截器接口. 二.在 springMVC 的xml配置文件中,配置我们创建的拦截器对象及其拦截目 ...

  2. swagger学习2

    转:http://blog.csdn.net/fansunion/article/details/51923720 写的非常好,非常详细,推荐!!!! 最常用的5个注解 @Api:修饰整个类,描述Co ...

  3. java md5加密 不依赖base64包

    /** * MD5 加密 */ private String getMD5Str(String str) { MessageDigest messageDigest = null; try { mes ...

  4. xcode 10 新特性

    这里主要介绍一下Xcode10 版本主要更新的内容.随着iOS12的发布,Xcode10已经可以从Mac App Store下载.Xcode10包含了iOS12.watchOS 5.macOS10.1 ...

  5. tr/td

    在HTML中,tr代表行,td代表列. 说明: 1.tr与td必须一起使用,并且输入的内容必须在td里面: 2.td必须在tr里面,表示在一行中的列: 3.在一个tr里面,有x个td,就表示在这一行里 ...

  6. 【HDU】6146 Pokémon GO

    [题意]一个2*n的网格,再保证步数最少的情况下,求从任意格出发遍历完所有格的方案数,格子八连通.n<=10000,T<=100. [算法]递推,DP [题解]原题链接:蓝桥杯 格子刷油漆 ...

  7. 【洛谷 P2480】 [SDOI2010]古代猪文(中国剩余定理,Lucas定理)

    题目链接 这题出的有点nb,PKU: Pig Kingdom University , NOIP: National Olympics in Informatic of Pigs... 题意:求\(G ...

  8. DDCTF - evil 一个伪装成docx的exe

    0x01 题目描述 题目比较简单,不过这种题感觉比单纯的逆向算法来有意思的多,所以记录一下~ 0x02 脱壳 先拖到IDA瞅一眼,发现加壳了 用PEID查一下是什么壳,但是没有查出来.使用String ...

  9. RabbitMQ消息队列(五): 主题分发

    1. 主题(Topics): fanout模式只能进行简单的广播,direct模式虽然在过滤上进行了一定的提升,但是不能支持复杂的条件, 比如我们的日志消息,现在不仅要知道消息级别,也要知道消息来源. ...

  10. Windows下搭建本地SVN服务器【转】

    转自:http://www.linuxidc.com/Linux/2015-01/111563.htm 本文介绍Windows下搭建本地SVN服务器的方法,网上资料比较少也比较旧,大都介绍的是旧版本S ...