码迷,mamicode.com
首页 > 其他好文 > 详细

luogu P4566 [CTSC2018]青蕈领主

时间:2020-06-02 20:33:03      阅读:82      评论:0      收藏:0      [点我收藏+]

标签:分治   子集   lock   lse   不能   mod   main   ret   href   

luogu

冷静分析这题有没有什么奇怪的性质

小绿觉得被别的“连续”区间包含住的“连续”区间不够优秀

这里不妨考虑“连续”区间的包含关系.对于两个有交的“连续”区间,如果没有包含关系就是不合法的,因为“连续”区间中所有数在值域上是连续的一段,那把这两个“连续”区间的值域段拿出来,这两段一定有交(因为在原序列上就有交,在值域上同样有).所以如果把靠后的“连续”区间的左端点延伸到靠前的“连续”区间的左端点,新的区间值域等于之前两个值域段的并,它也是连续的,所以原靠后区间就不是极长的“连续”区间

那么对于一个排列,去掉最后一个元素后,剩下部分可以划分成若干极长“连续”区间,然后对于每个“连续”区间递归下去也可以得到类似结果,所以对于一个极长“连续”区间,我们从它的右端点向最后一个元素后,剩下的极长“连续”区间右端点连边,然后递归,就可以得到一个树形结构.现在要利用这个树形结构算答案,dfs整棵树,对于当前节点,因为要满足不存在 长度\(>1\)的不是整个区间的 “连续”区间,所以把不能存在一些相邻区间满足值域连续;又因为它的每个儿子节点代表的极长“连续”区间都是值域上连续的一段,所以可以把每个区间缩成一个数,这时加上最后一个位置,原问题可以就对应到一个长度为\(i\)的区间,满足不存在 长度\(>1\)的不是整个区间的 “连续”区间,记长度为\(i\)的这样排列数为\(f_i\),这个点就可以给答案乘上\(f_{|son(x)|+1}(son(x)\)\(x\)儿子集合\()\)

现在考虑求\(f_i\).如果我们枚举所有长度为\(i\)的排列,那么这个排列去掉最后一位后,前面会划分成若干极长“连续”区间,我们要的是前面极长“连续”区间长度均为1的,等价于前面被划分成恰好\(i-1\)段.所以可以用总方案减掉前面划分段数更小的方案得到\(f_i\),设\(g_{i,j}\)为长度为\(i\),且划分成\(j\)段,每段为一个排列的方案数(每段的排列代表一个值域连续的段).求不合法方案的时候同样可以把每一段缩成一个数,因为要满足一些相邻段不可以为“连续”区间,所以有\(f_i=i!-\sum_{j=1}^{i-2} g_{i-1,j}f_{j+1}\)

考虑更优秀的做法,直接从\(f\)入手.为了方便,我们在原排列\(p\)的逆排列\(p^{-1}\)上考虑,那么原来的“连续”区间可以唯一对应逆排列的“连续”区间,原问题可以改为求满足不存在不包括最大值的极长“连续”区间的排列个数.对于长度为\(i\)的合法排列,我们考虑删掉里面的1,然后把前后拼起来得到长度为\(i-1\)的序列列

  • 如果新排列还是合法的,那么一定满足12不相邻,这样的排列数为\((i-2)f_{i-1}\)

  • 如果新排列不是合法的,那么只会有一个不包括最大值的极长“连续”区间,我们枚举其长度\(l(l>1)\),方案数的话,对于这个长度\(i-1\)的排列同样可以把这个长度\(l\)区间缩成一个数,对应一个长度\(i-l\)合法排列;然后对于这个段,先考虑其值域\([x,x+l-1]\),需要满足\(x>2\&\&x+l-1<i\),前者因为如果这个段中有2,加上1后还是连续的不合法段,后者是因为它不包括最大值,所以\(x\)取值有\(i-l-2\)种,然后考虑这个段的数相对顺序,由于加入1后这个段没有包含1的“连续”区间,所以可以把1认为是段内最大值,对应一个长度\(l+1\)合法排列.这部分贡献为\((i-2)f_{i-1}+\sum_{l=2}^{i-2}(i-l-2)f_{i-l}f_{l+1}\)

然后分治FFT优化\(f_i=(i-2)f_{i-1}+(i-2)f_{i-1}+\sum_{l=2}^{i-3}(i-l-2)f_{i-l}f_{l+1}\)即可

#include<bits/stdc++.h>
#define LL long long
#define db double

using namespace std;
const int N=50000+10,M=(1<<17)+10,mod=998244353;
int rd()
{
	int x=0,w=1;char ch=0;
	while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) w=-1;ch=getchar();}
	while(ch>=‘0‘&&ch<=‘9‘){x=x*10+(ch^48);ch=getchar();}
	return x*w;
}
int to[N],nt[N],hd[N],tot=1;
void adde(int x,int y){++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;}
int n,T,sz[N],a[N],g[N],ans,fac[N],st[N],tp;
void dfs(int x)
{
	sz[x]=1;
	for(int i=hd[x];i;i=nt[i])
	{
		int y=to[i];
		dfs(y),++sz[x];
	}
	ans=1ll*ans*g[sz[x]]%mod;
}
int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;}return an;}
int ginv(int a){return fpow(a,mod-2);}
int W[16],iW[16],rdr[M];
void ntt(int *a,int n,bool op)
{
	int l=0,y;
	while(1<<l<n) ++l;
	for(int i=0;i<n;++i)
	{
		rdr[i]=(rdr[i>>1]>>1)|((i&1)<<(l-1));
		if(i<rdr[i]) swap(a[i],a[rdr[i]]);
	}
	for(int i=1,p=0;i<n;i<<=1,++p)
	{
		int ww=op?W[p]:iW[p];
		for(int j=0;j<n;j+=i<<1)
			for(int k=0,w=1;k<i;++k,w=1ll*w*ww%mod)
				y=1ll*a[j+k+i]*w%mod,a[j+k+i]=(a[j+k]-y+mod)%mod,a[j+k]=(a[j+k]+y)%mod;
	}
	if(!op) for(int i=0,w=ginv(n);i<n;++i) a[i]=1ll*a[i]*w%mod;
}
int aa[M],bb[M];
void sov(int l,int r)
{
	if(l==r)
	{
		if(l>2) g[l]=(1ll*(l-2)*g[l-1]+g[l])%mod;
		return;
	}
	int mid=(l+r)>>1;
	sov(l,mid);
	int l1=l>1?r+1-l:mid-l+1,len=1;
	while(len<=l1+l1) len<<=1;
	for(int i=max(l,3);i<=mid;++i) aa[i-l]=g[i];
	for(int i=3;i<=l1;++i) bb[i]=1ll*g[i]*(i-2)%mod;
	ntt(aa,len,1),ntt(bb,len,1);
	for(int i=0;i<len;++i) aa[i]=1ll*aa[i]*bb[i]%mod;
	ntt(aa,len,0);
	for(int i=mid+1;i<=r;++i) g[i]=(g[i]+aa[i-l+1])%mod;
	memset(aa,0,sizeof(int)*len),memset(bb,0,sizeof(int)*len);
	if(l>1)
	{
		for(int i=max(l,3);i<=mid;++i) aa[i-l]=1ll*g[i]*(i-2)%mod;
		for(int i=3;i<=l1;++i) bb[i]=g[i];
		ntt(aa,len,1),ntt(bb,len,1);
		for(int i=0;i<len;++i) aa[i]=1ll*aa[i]*bb[i]%mod;
		ntt(aa,len,0);
		for(int i=mid+1;i<=r;++i) g[i]=(g[i]+aa[i-l+1])%mod;
		memset(aa,0,sizeof(int)*len),memset(bb,0,sizeof(int)*len);
	}
	sov(mid+1,r);
}

int main()
{
	for(int i=1,p=0;p<16;i<<=1,++p)
		W[p]=fpow(3,(mod-1)/(i<<1)),iW[p]=ginv(W[p]);
	T=rd(),n=rd();
	fac[0]=1;
	g[1]=1,g[2]=2;
	sov(1,n);
    /*for(int i=3;i<=n;++i)
	{
		LL na=(LL)(i-2)*g[i-1];
		for(int j=2;j<=i-3;++j)
			na+=(LL)(i-j-2)*g[i-j]*g[j+1];
		g[i]=na%mod;
	}*/
	while(T--)
	{
		for(int i=1;i<=n;++i) a[i]=rd();
		bool ok=1;
		memset(hd,0,sizeof(int)*(n+1)),tot=1;
		ans=1,tp=0;
		for(int i=n;i;--i)
		{
			while(tp&&st[tp]-a[st[tp]]+1>i) --tp;
			if(tp) adde(st[tp],i),ok&=st[tp]-a[st[tp]]+1<=i-a[i]+1;
			else if(i<n) ok=0;
			st[++tp]=i;
		}
		if(!ok){puts("0");continue;}
		dfs(n),printf("%d\n",ans);
	}
	return 0;
}

luogu P4566 [CTSC2018]青蕈领主

标签:分治   子集   lock   lse   不能   mod   main   ret   href   

原文地址:https://www.cnblogs.com/smyjr/p/13033483.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!