题解:CF1988F Heartbeat

CF1988F 题解

本题解参考官方做法

题面

原题传送门

题意

按如下方式定义一个排列 p1n 的权值:

  1. 定义前缀最大值 pi 满足 pi=imaxj=1pj,记 p 的前缀最大值的个数为 x
  2. 定义后缀最大值 pi 满足 pi=nmaxj=ipj,记 p 的后缀最大值的个数为 y
  3. 定义 z=i=2n[pi1<pi]

则排列 p 的权值为 ax×by×cz

其中 a,b1 开始输入,c0 开始输入。

前置知识

本题要用到组合数,组合数要是在转移方程里面进行暴力计算无疑会增加时间复杂度,所以考虑预处理。

那么组合数预处理其实就是用到这个公式:Cmn=Cmn1+Cm1n1,就开个二维数组预处理即可。

当然,既然是前置知识那就简单的证明一下,其实证明方法有两种,第一种是杨辉三角,第二种暴力拆开,这里使用第二种。

Cm1n1+Cmn1=(n1)×(n2)××(nm+1)(m1)!+(n1)×(n2)××(nm)m!=(n1)×(n2)××(nm+1)×(nm+m)m!=n×(n1)×(n2)××(nm)m!=Cmn

思路

首先看到题目很自然的想到了 DP,观察题目发现这题对答案有影响的就是前后缀最大值和上升点(后一个点比前一个点大即满足条件三的点对)的个数。

于是设 fn,i,j 表示 1n 的所有排列中有 i 个前缀最大值,j 个上升点的排列个数,gn,i,j 表示 1n 的所有排列中有 i 个后缀最大值,j 个上升点的排列个数。

由对称性可知,gn,i,j=fb,i,nj1,于是在打代码的时候就不需要 g,可以将它转化为 f

接下来考虑 f 的转移方程式。

首先先思考如何从 n1 个数转变成 n 个数是比较方便列式子的,容易想到,可以是将之前旧的 1n1 的排列中的每一个数都加一,于是就空出了一个 1 的位置,因为原数列都加上了一,相对大小关系不变,所以只用考虑插入这个新的 1 会产生的影响即可。

  1. 1 在上升点对的的中间或者在最后,因为 1 是最小的,所以这两种情况均不影响前缀最大值和上升点对数(1 和原本的上升点对中大的数组成一对,但拆散了原本的那一对,所以不影响),于是就有 fn,i,jfn,i,j+fn1,i,j×(j+1)。(j 是上升点的个数,1 是放在最后一个的情况。)
  2. 1 在最前面的位置,这样的话 1 会添加一个本身的前缀最大值,以及和原本数列的第一个数成为一个上升点对,(因为 1 是最小的。)于是 fn,i+1,j+1fn,i+1,j+1+fn1,i,j
  3. 1 在其他的 nj2 个位置的之一,因为 1 是最小的,所以 1 放在其他位置不会产生新的前缀最大值,会和插在的位置后面的那一个数产生一个新的上升点对,于是 fn,i,j+1fn,i,j+1+fn1,i,j×(nj2)

如果考虑插入 n 的话可能也可以,本人没试过,欢迎各位大佬尝试。

当然,这样子的是远远不足以通过题目的,也配不上这黑的难度,n3 的空间复杂度接受不了,不过发现三个转移方程的第一维都只与上一个状态有关,于是就考虑使用滚动数组来优化空间,这样空间复杂度就可以接受了。

接下来考虑如何统计答案。

首先想到 n 是整个数列中最大的数,必定会产生新的前后缀最大值,且继续往 n 的两边遍历是不会再出现前后缀最大值,所以考虑将数列分成 n 左边的一段,和 n 右边的一段。于是就可以得到如下方程。

ansn=np=1p1i=0npj=0p1x=0npy=0Cp1n1×fp1,i,x×gnp,j,y×ai+1×bj+1×cx+y+[p>1]

先来解释下,其实就是枚举 n 的一边的数字的情况(自然另一边也就出来了),再乘以相应的方案数,当然,如果 n1 这个位置不产生的上升点对,所以只有不在 1 时才会产生上升点对。

这个时间复杂度肯定是不够的,所以考虑降维。

发现 fp1,i,x,gnp,j,yai+1,bj+1 分别配对只有三个不同的变量,于是就可以预处理 ui,x=np=1fp1,i,x×ai+1,vj,y=np=1gnp,j,y×bj+1

(为什么是三个呢?因为预处理的时间复杂度最多是 n3 的,而两个不同的找不到。)

于是方程就变为 ansn=np=1p1x=0npy=0Cp1n1×up1,x×vnp,y×cx+y+[p>1]

但是这还是不够的,因为题目要求的是长度小于等于 n 的所有排列,外面还得套上一个枚举 n 的长度,所以还得再降一维。

观察发现 up1,xcx+y+[p>1] 只有三个不同的变量,也有可以预处理啦!将他们的积存进 wp1,y 中,于是,最终得到了一个真正正确的式子。

ansn=np=1npy=0Cp1n1×wp1,y×vnp,y

上代码!!!

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define ni i&1
#define li (i-1)&1 
using namespace std;
const int MN=705;
const int mod=998244353;
ll n,ans,a[MN],b[MN],c[MN],C[MN][MN],f[2][MN][MN],u[MN][MN],v[MN][MN],w[MN][MN];
void write(ll n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
int main(){
// 	freopen("heart.in","r",stdin);
// 	freopen("heart.out","w",stdout); 
	n=read();
	for(int i=1; i<=n; i++) a[i]=read();
	for(int i=1; i<=n; i++) b[i]=read();
	for(int i=0; i<n; i++) c[i]=read();
	for(int i=0; i<=n; i++){C[i][0]=1;for(int j=1; j<=i; j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;}
	u[0][0]=a[1];v[0][0]=b[1];
	for(int i=1; i<=n; i++){
		if(i==1) f[1][1][0]=1;
		else{
			for(int j=1; j<i; j++) for(int k=0; k<i; k++) f[ni][j][k]=0;
			for(int j=1; j<i; j++) for(int k=0; k<i-1; k++){
				f[ni][j][k]=(f[ni][j][k]+f[li][j][k]*(k+1)%mod)%mod;
				f[ni][j+1][k+1]=(f[ni][j+1][k+1]+f[li][j][k])%mod;
				f[ni][j][k+1]=(f[ni][j][k+1]+f[li][j][k]*(i-k-2)%mod)%mod;
			}
		}
		for(int j=1; j<=i; j++) for(int k=0; k<i; k++){
			u[i][k]=(u[i][k]+f[ni][j][k]*a[j+1]%mod)%mod;
			v[i][k]=(v[i][k]+f[ni][j][i-k-1]*b[j+1]%mod)%mod;
		}
	}
	for(int i=0; i<=n; i++) for(int j=0; j<=i; j++) for(int k=0; k<n-j; k++) w[i][k]=(w[i][k]+u[i][j]*c[j+k+(i>0)]%mod)%mod;
	for(int i=1; i<=n; i++){
		ans=0;
        for(int p=1; p<=i; p++) for(int y=0; y<=i-p; y++) ans=(ans+w[p-1][y]*v[i-p][y]%mod*C[i-1][p-1]%mod)%mod;
		write(ans);putchar('\n');
	}
	return 0;
}
posted @   naroto2022  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
战斗是残酷的,无法做出多余的考虑!