zoukankan      html  css  js  c++  java
  • AGC054F

    有两个长度分别为(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)

    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 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;
    }
    
    
  • 相关阅读:
    关于在windows平台下将应用制作成windows服务及服务依赖的感想
    mysql 变量赋值的三种方法
    如何上传本地jar至远程仓库供其他项目使用
    maven的标准
    修改idea的缓存
    前端的网站
    读取简单的xml
    IDEA 自动设置compile target变成1.5
    注解导出优化版(推荐,十分强大)
    Linux命令干货!!!最常用的命令
  • 原文地址:https://www.cnblogs.com/jz-597/p/14966859.html
Copyright © 2011-2022 走看看