AGC054F - Decrement

有两个长度分别为\(n\)\(n-1\)的数组\(A,B\)

每次可以选择\(l<r\),操作\(A_l,A_r\)\(B_l,B_{l+1},\dots,B_{r-1}\),将其减一。要求操作之后不能出现负数。

问在操作次数最多的前提下,操作之后不同的数组\(a\)的方案数。

\(n\le 2*10^5\)


假设\(A_i,B_i\)最终各减了\(a_i,b_i\)。考虑\(a_i,b_i\)能够减完的充要条件:

\(b_0=b_n=0\)

  1. \(2|(a_i+b_{i-1}+b_i)\)
  2. \(\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 i\in(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_i\ge 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)\n",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\n",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\n",ans);
	return 0;
}

posted @ 2021-07-03 17:29  jz_597  阅读(176)  评论(0编辑  收藏  举报