2023暑期牛客多校 4

又是被创的一天呢

J.Qu’est-ce Que C’est?

题意:找到满足如下公式的数组的数量,对998244353取模。
·对于所有的 1 ≤ i ≤ n 1\leq i \leq n 1in,满足 − m ≤ a i ≤ m -m \leq a_i \leq m maim
·对于所有的 1 ≤ l < r ≤ n 1\leq l < r \leq n 1l<rn,满足 ∑ i = l r a i ≥ 0 \sum_{i=l}^ra_{i} \geq 0 i=lrai0

思路:一看 1 ≤ n , m ≤ 5000 1 \leq n,m \leq 5000 1n,m5000应该是个DP了。赛时想了两种构造方法。其中一种十分愚蠢,纪念一下。
以下为愚蠢发言
f ( i , j ) f(i,j) f(i,j) 在第 i i i个位置上的数为 j j j的方案数量。
主要思想是枚举每一个位置上的每一个数字是什么,然后进行分类讨论转移。
当我们枚举的数字为非负数时 也就是 i ≥ 0 i \geq 0 i0时转移的式子很明显:
f ( i , j ) = ∑ k ≥ − j m f ( i − 1 , k ) f(i,j)=\sum_{k\geq -j}^{m}f(i-1,k) f(i,j)=kjmf(i1,k)

j < 0 j <0 j<0时 我们不能从 i − 1 i-1 i1的位置转移,考虑从 i − 2 i-2 i2的位置得到答案。观察题目中的第二个要求,可以得到,不可能有两个负数相邻的结论(看不懂英语)。所以如果当前枚举的值为负数,那么位置 i − 1 i-1 i1上的值必然为正数,位置 i − 2 i-2 i2上的值正负均有可能,只要保证三个数仍满足题目要求就可以进行转移。
j = − 1 j=-1 j=1可以列出如下式子:

f ( i , − 1 ) = 1 ∗ f ( i − 2 , 1 − m ) + 2 ∗ f ( i − 2 , 1 − m + 1 ) + ⋯ + m ∗ f ( i − 2 , m ) f(i,-1)=1*f(i-2,1-m)+2*f(i-2,1-m+1)+\cdots+m*f(i-2,m) f(i,1)=1f(i2,1m)+2f(i2,1m+1)++mf(i2,m)

最后一项的 m m m 可以写成 1 − m + m − 1 1-m+m-1 1m+m1可能比较好理解
那么 j = − 2 j=-2 j=2可以列出如下式子:

f ( i , − 2 ) = 1 ∗ f ( i − 2 , 2 − m ) + 2 ∗ f ( i − 2 , 2 − m + 1 ) + ⋯ + ( m − 1 ) ∗ f ( i − 2 , m ) f(i,-2)=1*f(i-2,2-m)+2*f(i-2,2-m+1)+\cdots+(m-1)*f(i-2,m) f(i,2)=1f(i2,2m)+2f(i2,2m+1)++(m1)f(i2,m)

把上述式子单独拿一个部分出来 b ∗ f ( i − 2 , k ) b*f(i-2,k) bf(i2,k)来看, b b b表示第 i i i个位置可以选多少个数字, k k k i − 2 i-2 i2所选的数字, − 1 -1 1则是第 i i i位的数字。

发现在某一节的时候, b b b保持不变,而在之前 b b b,是属于一个递增1的状态。可以看出这个点就是 k = 0 k=0 k=0,在 k = 0 k=0 k=0及以后 b = m + j − 1 b=m+j-1 b=m+j1

整理一下,就可以得到:

f ( i , j ) = 1 ∗ f ( i − 2 , − j − m ) + ⋯ + ( m + j − 1 ) ∗ f ( i − 2 , m ) f(i,j)=1*f(i-2,-j-m)+\cdots+(m+j-1)*f(i-2,m) f(i,j)=1f(i2,jm)++(m+j1)f(i2,m)

然后就可以进行愉快的转移了
事实上一点都不愉快,暴力转移复杂度达到了 O ( n 3 ) O(n^3) O(n3)
所以要考虑前缀和优化,我们要维护每个位置对于 − m -m m m m m的前缀和 p r e pre pre − m -m m − 1 -1 1的后缀和 s u f suf suf − m -m m − 1 -1 1的后缀和的后缀和 s u f f suff suff

根据上述式子可以写出:
j < 0 j<0 j<0
f ( i , j ) = ( p r e ( i − 2 , m ) − p r e ( i − 2 , − 1 ) ) ∗ ( m + j + 1 ) + s u f f ( i − 2 , 1 − j ) f(i,j)=(pre(i-2,m)-pre(i-2,-1))*(m+j+1)+suff(i-2,1-j) f(i,j)=(pre(i2,m)pre(i2,1))(m+j+1)+suff(i2,1j)

j ≥ 0 j\geq 0 j0
f ( i , j ) = ( p r e ( i − 2 , m ) − p r e ( i − 2 , m − j ) ) f(i,j)=(pre(i-2,m)-pre(i-2,m-j)) f(i,j)=(pre(i2,m)pre(i2,mj))

//It's better to have sex than to do questions
#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=5e3+5;
const ll md=998244353;
const ll inf=1e18;
const ll eps=1e-9;
const double E=2.718281828;
int f[N][N*2],n,m,suff[N][N*2],pre[N][N*2];

void solve(){
	cin>>n>>m;
	int dif=m+1;
	f[0][0+dif]=1; 
	for(int j=-m+dif;j<=m+dif;j++){
		pre[0][j]=(pre[0][j-1]+f[0][j])%md;
	}
	for(int j=-1+dif;j>=-m+dif;j--){
		suff[0][j]=f[0][j]+suff[0][j+1];
		suff[0][j]%=md;
	}
	for(int j=-1+dif;j>=-m+dif;j--){
		suff[0][j]+=suff[0][j+1];
		suff[0][j]%=md;
	}
	for(int i=-m+dif;i<=m+dif;i++){
		f[1][i]=1;
	}
//	f[0][0+dif]=1;
	for(int j=-m+dif;j<=m+dif;j++){
		pre[1][j]=(pre[1][j-1]+f[1][j])%md;
	}
	for(int j=-1+dif;j>=-m+dif;j--){
		suff[1][j]=f[1][j]+suff[1][j+1];
		suff[1][j]%=md;
	}
	for(int j=-1+dif;j>=-m+dif;j--){
		suff[1][j]+=suff[1][j+1];
		suff[1][j]%=md;
	}
	for(int i=2;i<=n;i++){
		for(int j=-m+dif;j<=m+dif;j++){
			if(j>=0+dif){
				f[i][j]=(pre[i-1][m+dif]-pre[i-1][-1*(j-dif)+dif-1]+md)%md; //query(i-1,-1*(j-dif)+dif,m+dif);
			}else{
//				cout<<i<<" "<<j-dif<<" "<<(m+(j-dif)+1)<<endl;
				f[i][j]=(pre[i-2][m+dif]-pre[i-2][0+dif-1]+md)%md*(m+(j-dif)+1)%md;
				f[i][j]+=suff[i-2][dif*2-j-m];
                f[i][j]%=md;
			}
		}
		for(int j=-m+dif;j<=m+dif;j++){
			pre[i][j]=(pre[i][j-1]+f[i][j])%md;
		}
		for(int j=-1+dif;j>=-m+dif;j--){
			suff[i][j]=f[i][j]+suff[i][j+1];
			suff[i][j]%=md;
		}
		for(int j=-1+dif;j>=1-m+dif;j--){
			suff[i][j]+=suff[i][j+1];
			suff[i][j]%=md;
		}
	}
//	for(int i=1;i<=n;i++){
//		for(int j=-m+dif;j<=m+dif;j++){
//			cout<<f[i][j]<<" ";
//		}
//		cout<<endl;
//	}
//	cout<<endl;
	ll ans=0;
	for(int i=-m+dif;i<=m+dif;i++){
		ans=(ans+f[n][i])%md;
	}
	cout<<ans<<"\n";
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t=1;
//	cin>>t;
	while(t--){
		solve();
	}
}

第二种想法:
f ( i , j ) f(i,j) f(i,j)为第 i i i个位置,最小前缀和为 j j j的方案数。赛时有这种想法但是没有完整推出来,队友给了一个先转移在消去不合法情况的式子,花了好久证明他有bug,但是来不及改了,改着改着就变成了上面那个“睿智”做法。(拷打自己!!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值