$\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}$,但我并没有找到证明,看看以后有没有机会填坑,这样做就可以只做一次多项式求逆

#include<stdio.h>
#include<string.h>
const int mod=998244353,maxn=1048576;
typedef long long ll;
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
int rev[maxn],N,iN;
void pre(int n){
	int i,k;
	for(N=1,k=0;N<n;N<<=1)k++;
	for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
	iN=pow(N,mod-2);
}
void swap(int&a,int&b){a^=b^=a^=b;}
void ntt(int*a,int on){
	int i,j,k,t,w,wn;
	for(i=0;i<N;i++){
		if(i<rev[i])swap(a[i],a[rev[i]]);
	}
	for(i=2;i<=N;i<<=1){
		wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
		for(j=0;j<N;j+=i){
			w=1;
			for(k=0;k<i>>1;k++){
				t=mul(w,a[i/2+j+k]);
				a[i/2+j+k]=de(a[j+k],t);
				a[j+k]=ad(a[j+k],t);
				w=mul(w,wn);
			}
		}
	}
	if(on==-1){
		for(i=0;i<N;i++)a[i]=mul(a[i],iN);
	}
}
int t[maxn];
void getinv(int*a,int*b,int n){
	if(n==1){
		b[0]=pow(a[0],mod-2);
		return;
	}
	int i;
	getinv(a,b,n>>1);
	pre(n<<1);
	memset(t,0,N<<2);
	memcpy(t,a,n<<2);
	ntt(t,1);
	ntt(b,1);
	for(i=0;i<N;i++)b[i]=mul(b[i],2-mul(b[i],t[i]));
	ntt(b,-1);
	for(i=n;i<N;i++)b[i]=0;
}
int a[maxn],b[maxn],c[maxn];
int main(){
	int n,k,i,ans;
	scanf("%d",&n);
	for(k=1;k<=n;k<<=1);
	a[0]=1;
	for(i=1;i<=n;i++){
		a[i]=mul(i,a[i-1]);
		b[i]=mul(i,a[i]);
	}
	pre(k<<1);
	ntt(a,1);
	for(i=0;i<N;i++)a[i]=mul(a[i],a[i]);
	ntt(a,-1);
	for(i=0;i<=n;i++)b[i]=de(a[i],b[i]);
	getinv(b,c,k);
	ans=0;
	for(i=0;i<=n;i++)ans=ad(ans,mul(a[i],c[n-i]));
	printf("%d",ad(ans,mod));
}

[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. recycleview的基础Adapter

    .封装了一个基础的adapter.,用于recycleview的快捷使用有BaseAdapter,BaseViewHolder,PAdapter,MainActivity public abstrac ...

  2. 牛客 国庆七天乐 day1 L

    https://www.nowcoder.com/acm/contest/201/L 题意:给你两条平行的直线和n个圆,在直线上面行走和在圆上和在圆内行走不需要耗费体力,除了这些区域外平面上经过任意两 ...

  3. springboot中 后端跨域的实现配置

    在springboot的启动文件中,添加此内容,可以允许跨域

  4. com.mongodb.MongoException$CursorNotFound: cursor not found on server异常处理

    java链接MongoDB处理大量数据时经常碰到cursor not found 的异常,其实是超时所致 Exception in thread "main" com.mongod ...

  5. PHP代码优化小笔记

    1.十万级以上次执行情况,方法可以被静态化,考虑声明为静态.html静态页面速度更快 2.echo 替换print:echo时逗号连接符替换点号连接符 3.循环之前设置循环最大次数,循环参数不要使用函 ...

  6. C# windows application Hello World

    创建一个Windows application项目,然后可以调用里面的工具来生成代码. using System; using System.Collections.Generic; using Sy ...

  7. tomcat编码配置

    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" ...

  8. Http/2 升级指南

    [转]http://www.syyong.com/architecture/http2.html HTTP/2(最初名为HTTP/2.0)是 WWW 使用的 HTTP 网络协议的主要版本. 它来自早先 ...

  9. MyBatis系列三 之 使用getMapper剔除掉Dao的实现类

    MyBatis系列三  之  使用getMapper剔除掉Dao的实现类 我们在系列一 中 我们使用的是Dao的实现类   来操作底层数据库,今天我们使用getMapper()来替换Dao的实现类, ...

  10. bzoj 2064 DP

    这道题可以抽象成两个数列,将一个数列变换为另一个 数列的代价最小 首先我们可以处理出所有的状态代表,对于每个状态 用二进制来表示,代表的是两个数列中的每一项选还是不选 那么答案最多为n1+n2-2,也 ...