CF1988F Heartbeat

CF1988F Heartbeat

My Blogs

CF1988F Heartbeat

最大值把序列分成两部分,前一部分对前缀最大值个数有贡献,后一部分对后缀最大值个数有贡献,可以分开算。

fi,j,k 表示 [1,i] 的排列,有 j 个前缀最大值,k 个上升的位置的方案数。不断向右边加数不好做,不断加入 n+1 也不好做,因为可能前缀最大值个数会变少。所以考虑不断的加入新的 1,把原来的值域整体平移。转移比较平凡:

  1. 1 放在最前面:fi+1,j+1,k+1fi,j,k
  2. 1 放在最后面:fi+1,j,kfi,j,k
  3. 1 放在上升的位置中间:fi+1,j,kfi,j,k×j
  4. 1 放在下降的位置中间:fi+1,j+1,kfi,j,k×(i1j)

预处理这部分的复杂度是 O(n3) 的。

接下来考虑把两个拼起来。因为前缀最大值个数和后缀最大值个数是独立的,所以可以先把 A,B 序列的权值直接乘进去。一个上升位置为 j 的长度为 i 的序列显然有 i1j 个下降位置。所以前缀的方案数把 k 全部变成 i1k 就是后缀的方案数。

处理出 li,j 表示长度为 i 的序列有 j 个上升位,统计前缀最大值个数的贡献。ri,j 表示长度为 i 的序列有 j 个上升位,统计后缀最大值个数的贡献:

li,k=j=1ifi,j,k×ajri,i1k=j=1ifi,j,k×bj

接下来只需要统计上升位 C 序列的贡献。设 ansi,j 表示长度为 i 的序列有 j 个上升位,统计了前后缀最大值个数的贡献和。朴素的拼起来:

ansi+j+1,k+o+1li,k×rj,o×(i+ji)

ans 的两个下标都 +1 是因为强制要求最大值在中间。暴力做复杂度是 O(n4)

如何优化?首先变形一下式子。

ansi+j+1,k+o+1(i+j)!li,ki!×rj,oj!

发现转移是二维卷积的形式,可以用拉插优化。首先设 Li(x)=j=0i1li,jxj,Ri(x)=j=0i1ri,jxj。注意这里的 l,r 都要除一个阶乘,设 Ansi(x)=j=0i1ansi,jxj,则:

Ansi+j(x)Li(x)Rj(x)

相乘指多项式乘法,加法指对应系数相加。Ans(x)n1 次多项式,可以先求出 L(x),R(x)x[1,n]n 个地方的点值,设其为 Li(x),Ri(x),然后 Ans(x) 的点值就是对应位相乘再相加:

Ansi+j(x)=Li(x)Ri(x)

相乘指对应位置相乘,相加指对应位置相加。这样求出 Ansin 处点值,再用拉插把真实的多项式插出来,这样系数就是做二维卷积的结果。求点值复杂度 O(n3),相乘复杂度 O(n3),拉插求系数复杂度 O(n3)。常数非常的大,但是不太用动脑子。

int a[710],b[710],c[710],A[710],B[710][710];
int n,now,pre,f[2][710][710],l[710][710],r[710][710];
int vl[710],vr[710],x[710][710],d[710];
int fr[710],inv[710];
inline int C(int n,int m){return m<0||m>n?0:Cmul(fr[n],inv[m],inv[n-m]);}
inline void mian()
{
	read(n),f[1][0][1]=1,fr[0]=inv[0]=1;
	for(int i=1;i<=n;++i)fr[i]=Cmul(fr[i-1],i);
	inv[n]=power(fr[n],MOD-2);
	for(int i=n-1;i>0;--i)inv[i]=Cmul(inv[i+1],i+1);
	for(int i=1;i<=n;++i)read(a[i]);
	for(int i=1;i<=n;++i)read(b[i]);
	for(int i=0;i<n;++i)read(c[i]);
	l[1][0]=a[1],r[1][0]=b[1];
	l[2][1]=a[2],r[2][0]=b[2];
	now=1,pre=0;
	for(int i=1;i<n;++i)
	{
		swap(pre,now),memset(f[now],0,sizeof(f[now]));
		for(int j=0;j<i;++j)
		{
			for(int k=1;k<=i;++k)
			{
				Madd(f[now][j+1][k+1],f[pre][j][k]);//1 placed in front
				Madd(f[now][j][k],f[pre][j][k]);//1 placed in end
				Madd(f[now][j][k],Cmul(j,f[pre][j][k]));//1 placed between a up
				Madd(f[now][j+1][k],Cmul(i-1-j,f[pre][j][k]));//1 placed between a down
			}
		}
		for(int j=0;j<i+1;++j)for(int k=1;k<=i+1;++k)
		Madd(l[i+2][j+1],Cmul(f[now][j][k],a[k+1])),
		Madd(r[i+2][i-j],Cmul(f[now][j][k],b[k+1]));
	}
	for(int i=1;i<=n;++i)for(int j=0;j<i;++j)Mmul(l[i][j],inv[i-1]);
	for(int i=1;i<=n;++i)for(int j=0;j<i;++j)Mmul(r[i][j],inv[i-1]);
	for(int X=1;X<=n+2;++X)
	{
		for(int i=1;i<=n;++i)
		{
			int v=0;
			for(int j=0,s=1;j<i;++j,s=Cmul(s,X))
			Madd(v,Cmul(s,l[i][j]));
			vl[i]=v,v=0;
			for(int j=0,s=1;j<i;++j,s=Cmul(s,X))
			Madd(v,Cmul(s,r[i][j]));
			vr[i]=v;
		}
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j+i-1<=n;++j)
			Madd(x[i+j-1][X],Cmul(vl[i],vr[j]));
		}
	}
	A[0]=1;
	for(int i=1;i<=n+2;++i)
	{
		for(int j=i;j>=1;--j)A[j]=Cdel(A[j-1],Cmul(i,A[j]));
		Mmul(A[0],MOD-i);
	}
	for(int j=1;j<=n+2;++j)
	{
		int c=power(MOD-j,MOD-2);B[j][0]=Cmul(A[0],c);
		for(int k=1;k<=n+1;++k)B[j][k]=Cmul(Cdel(A[k],B[j][k-1]),c);
	}
	for(int i=1;i<=n;++i)
	{
		memset(d,0,sizeof(d));
		for(int j=1,v;j<=n+2;++j)
		{
			v=1;
			for(int k=1;k<=n+2;++k)if(j!=k)Mmul(v,Cdel(j,k));
			v=Cmul(x[i][j],power(v,MOD-2));
			for(int k=0;k<=n;++k)Madd(d[k],Cmul(v,B[j][k]));
		}
		int ans=0;
		for(int j=0;j<n;++j)Madd(ans,Cmul(d[j],c[j]));
		write(Cmul(ans,fr[i-1]));
	}
}
posted @   WrongAnswer_90  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现

This blog has running: 562 days 10 hours 6 minutes 28 seconds

点击右上角即可分享
微信分享提示