又是被创的一天呢
J.Qu’est-ce Que C’est?
题意:找到满足如下公式的数组的数量,对998244353取模。
·对于所有的
1
≤
i
≤
n
1\leq i \leq n
1≤i≤n,满足
−
m
≤
a
i
≤
m
-m \leq a_i \leq m
−m≤ai≤m
·对于所有的
1
≤
l
<
r
≤
n
1\leq l < r \leq n
1≤l<r≤n,满足
∑
i
=
l
r
a
i
≥
0
\sum_{i=l}^ra_{i} \geq 0
i=l∑rai≥0
思路:一看
1
≤
n
,
m
≤
5000
1 \leq n,m \leq 5000
1≤n,m≤5000应该是个DP了。赛时想了两种构造方法。其中一种十分愚蠢,纪念一下。
以下为愚蠢发言
设
f
(
i
,
j
)
f(i,j)
f(i,j) 在第
i
i
i个位置上的数为
j
j
j的方案数量。
主要思想是枚举每一个位置上的每一个数字是什么,然后进行分类讨论转移。
当我们枚举的数字为非负数时 也就是
i
≥
0
i \geq 0
i≥0时转移的式子很明显:
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)=k≥−j∑mf(i−1,k)
当
j
<
0
j <0
j<0时 我们不能从
i
−
1
i-1
i−1的位置转移,考虑从
i
−
2
i-2
i−2的位置得到答案。观察题目中的第二个要求,可以得到,不可能有两个负数相邻的结论(看不懂英语)。所以如果当前枚举的值为负数,那么位置
i
−
1
i-1
i−1上的值必然为正数,位置
i
−
2
i-2
i−2上的值正负均有可能,只要保证三个数仍满足题目要求就可以进行转移。
设
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)=1∗f(i−2,1−m)+2∗f(i−2,1−m+1)+⋯+m∗f(i−2,m)
最后一项的
m
m
m 可以写成
1
−
m
+
m
−
1
1-m+m-1
1−m+m−1可能比较好理解
那么
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)=1∗f(i−2,2−m)+2∗f(i−2,2−m+1)+⋯+(m−1)∗f(i−2,m)
把上述式子单独拿一个部分出来 b ∗ f ( i − 2 , k ) b*f(i-2,k) b∗f(i−2,k)来看, b b b表示第 i i i个位置可以选多少个数字, k k k为 i − 2 i-2 i−2所选的数字, − 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+j−1
整理一下,就可以得到:
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)=1∗f(i−2,−j−m)+⋯+(m+j−1)∗f(i−2,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(i−2,m)−pre(i−2,−1))∗(m+j+1)+suff(i−2,1−j)
当
j
≥
0
j\geq 0
j≥0
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(i−2,m)−pre(i−2,m−j))
//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,但是来不及改了,改着改着就变成了上面那个“睿智”做法。(拷打自己!!)