有两个长度分别为(n)和(n-1)的数组(A,B)。
每次可以选择(l<r),操作(A_l,A_r)和(B_l,B_{l+1},dots,B_{r-1}),将其减一。要求操作之后不能出现负数。
问在操作次数最多的前提下,操作之后不同的数组(a)的方案数。
(nle 2*10^5)
假设(A_i,B_i)最终各减了(a_i,b_i)。考虑(a_i,b_i)能够减完的充要条件:
令(b_0=b_n=0)
- (2|(a_i+b_{i-1}+b_i))
- (max(a_i,b_{i-1},b_i)le frac{a_i+b_{i-1}+b_i}{2})
证明:
必要性:以(a_i,b_{i-1},b_i)的视角考虑,每次与其有关的操作一定是选择其中两个减一。
充分性:归纳。假如操作了(l,r),必须要满足:(forall iin(l,r),a_i<b_{i-1}+b_i)和(b_{l-1}<a_l+b_l,b_r<a_r+b_{r-1})。如果将(a_i=b_{i-1}+b_i)的点提取出来,任取相邻两个操作,就可以满足。((1)和(n)一定满足,所以至少两个)
先调整(B_i)和(A_i)。如果(B_{i-1}>A_i+B_i),则将其调整为(B_{i-1});(B_i)类似。搞完(B)之后,如果出现(A_ige B_{i-1}+B_i),可以将问题以(i)为分界分开,对于左边假装(A_i=B_{i-1}),对于右边假装(A_i=B_{i})。
然后要最大化(sum a_i)。对于一个子问题,数下(t_i=(A_i+B_{i-1}+B_i)mod 2=1)的个数(c),如果(2|c),把相邻的(t_i=1)的(i)两两配对,每对之间的(B)减一,此时(sum A_i)取到(sum A_i);否则至多取到(sum A_i-1),如果钦定(a_1=A_1-1),用类似的方法也可以达到这个界。
算方案:如果(2|c)则唯一;否则要对每个(i)判断一下能否取(a_i=A_i-1),这个东西做个前缀和后缀的DP,合并起来即可。具体:设(f_{i})表示(a_1,dots,a_i)取(A_1,dots,A_i),此时(b_i)可行的取值(区间+奇偶性),另一边类似。
题解说时间(O(n)),然而我不知道前面调整(B)的那部分怎么(O(n))做,我是直接跑最短路的……
using namespace std;
#include <bits/stdc++.h>
const int N=200005,mo=998244353;
typedef long long ll;
#define fi first
#define se second
#define mp make_pair
int n;
int a[N],b[N];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
void init(){
for (int i=0;i<=n;++i)
q.push(mp(b[i],i));
while (!q.empty()){
int x=q.top().se,s=q.top().fi;
q.pop();
if (s>b[x]) continue;
if (x>0 && a[x]+b[x]<b[x-1]){
b[x-1]=a[x]+b[x];
q.push(mp(b[x-1],x-1));
}
if (x<n && a[x+1]+b[x]<b[x+1]){
b[x+1]=a[x+1]+b[x];
q.push(mp(b[x+1],x+1));
}
}
}
struct status{int l,r,c;} f[N],g[N];
status merge(status x,status y){
status z;
z.c=x.c^y.c;
z.r=x.r+y.r;
if (max(x.l,y.l)<=min(x.r,y.r))
z.l=z.c;
else
z.l=min(abs(x.l-y.r),abs(x.r-y.l));
return z;
}
ll work(int l,int r){
if (l==r) return 1;
int c=0;
c^=a[l]+b[l]&1;
c^=a[r]+b[r-1]&1;
for (int i=l+1;i<r;++i)
c^=a[i]+b[i-1]+b[i]&1;
if (c^1)
return 1;
f[l-1]={0,0,0};
for (int i=l;i<r;++i){
f[i]=merge(f[i-1],(status){a[i],a[i],a[i]&1});
f[i].r=min(f[i].r,b[i]);
if (f[i].r&1^f[i].c)
f[i].r--;
}
g[r+1]={0,0,0};
for (int i=r;i>l;--i){
g[i]=merge(g[i+1],(status){a[i],a[i],a[i]&1});
g[i].r=min(g[i].r,b[i-1]);
if (g[i].r&1^g[i].c)
g[i].r--;
}
/*
for (int i=l;i<=r;++i){
printf("(%d,%d,%d) (%d,%d,%d)
",f[i].l,f[i].r,f[i].c,g[i].l,g[i].r,g[i].c);
}*/
ll cnt=0;
for (int i=l;i<=r;++i){
status t=merge(f[i-1],g[i+1]);
if (t.c==(a[i]-1&1) && (t.l<=a[i]-1 && a[i]-1<=t.r)){
//printf("%d
",i);
cnt++;
}
}
return cnt;
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<n;++i)
scanf("%d",&b[i]);
init();
ll ans=1;
int lst=1;
for (int i=1;i<=n;++i){
if (a[i]>=b[i-1]+b[i]){
a[i]=b[i-1];
ans=ans*work(lst,i)%mo;
lst=i;
a[i]=b[i];
}
}
ans=ans*work(lst,n)%mo;
printf("%lld
",ans);
return 0;
}