zoukankan      html  css  js  c++  java
  • Arc066_F Contest with Drinks Hard

    传送门

    题目大意

    有一个长为$N$的序列$A$,你要构造一个长为$N$的$01$序列使得$01$序列全部由$1$组成的子串个数$-$两个序列的对应位置两两乘积之和最大,每次独立的询问给定$pos,x$,求当$A_{pos}=x$时最大的答案,每次询问后将$A$复原。

    题解

    考虑不加入询问单纯地求原序列最大价值就变成了经典的斜率优化。

    设$S$为序列$A$的前缀和,$F_i$表示对于前$i$个位置其中第$i$个不选的最大价值,假设枚举$j$表示上一个不选的位置是$j$。

    那么$F_i=F_j-(S_{i-1}-S_j)+frac{(i-j-1)(i-j)}{2}$。

    这个式子可以优化,只需要维护$(j,F_j+S_j+frac{j(j+1)}{2})$的上凸壳即可。

    不难发现斜率$i$是递增的,所以具有决策单调性,用单调栈维护即可,可以做到$O(n)$。

    现在加入了限制将$A_{pos}$改为$x$,发现只有两种情况。

    第一种,不选$A_{pos}$,那么只需要预处理出前缀和后缀的$Dp$值$F$和$G$,那么答案就是$F_{pos}+G_{pos}$。

    第二种,选了$A_{pos}$,那么要满足选了某个区间$[l,r]$其中$lleq posleq r$。

    考虑预处理出$H_i$表示强制选第$i$个的答案,那么答案就是$H_i+A_{pos}-x$,我们现在只需要求出$H_i$即可。

    直接枚举跨越$i$的区间比较难办,考虑分治,每个分治区$[l,r]$只讨论选了上一个不选的$in[l-1,mid]$,最后一个选的$in[mid+1,r]$的答案,以及后缀意义下上一个不选的$in[mid+1,r+1]$,最后一个选的$in[l,mid]$的答案。

    每次分别求出了了一定覆盖$x(xin[l,mid])$的答案和一定覆盖$y(yin[mid+1,r])$,求出$x$的前缀最大值和$y$的后缀最大值,分别用于更新覆盖$x$的更新$y$的答案。

    复杂度$O(Nlog N+Q)$

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define INF 10000000000000ll
    #define M 300020
    using namespace std;
    namespace IO{
    	const int BS=(1<<20)+5; int Top=0;
    	char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1;
    	char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
    	void flush(){fwrite(OT,1,OS-OT,stdout);}
    	void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;}
    	void write(LL x){
    		if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
    		while(x) SS[++Top]=x%10,x/=10;
    		while(Top) Putchar(SS[Top]+'0'),--Top;
    	}
    	int read(){
    		int nm=0,fh=1; char cw=Getchar();
    		for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
    		for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
    		return nm*fh;
    	}
    }
    using namespace IO;
    int n,m,p[M];
    LL H[M],G[M],F[M],S[M],top,D[M],P[M];
    LL X[M],Y[M];
    #define calc(b,a) x[b]-S[a-1]+S[b]+((LL)(a-b-1)*(LL)(a-b)>>1)
    #define conv(a,b,c) ((double)(Y[b]-Y[a])/(double)(X[b]-X[a])>(double)(Y[c]-Y[b])/(double)(X[c]-X[b]))
    #define Transl(b,a) G[a+1]-S[a]+S[b]+F[b]+((LL)(a-b)*(LL)(a-b+1)>>1)
    #define Transr(b,a) F[a-1]-S[a]+S[b]+G[b]+((LL)(b-a)*(LL)(b-a+1)>>1)
    void init(LL *x){
    	S[top=1]=0,Y[0]=X[0]=S[0]=0;
    	for(LL i=1;i<=n+1;i++) S[i]=S[i-1]+p[i];
    	for(LL i=0;i<=n+1;i++){
    		while(top>1&&calc(P[top-1],i)>=calc(P[top],i)) top--;
    		if(top) x[i]=calc(P[top],i);else x[i]=0;
    		X[i]=i,Y[i]=x[i]+S[i]+((i*i+i)>>1);
    		while(top>1&&!conv(P[top-1],P[top],i)) top--; P[++top]=i;
    	}
    }
    void solve(int l,int r){
    	if(l==r){H[l]=max(H[l],F[l-1]+G[r+1]-p[l]+1);return;}
    	int mid=((l+r)>>1); solve(l,mid),solve(mid+1,r);
    	top=S[l-1]=S[r+1]=X[l-1]=X[r+1]=Y[l-1]=Y[r+1]=0;
    	D[l-1]=D[r+1]=-INF; top=0;
    	
    	for(int i=l;i<=r;i++) D[i]=-INF,S[i]=S[i-1]+p[i]; top=0;
    	for(int i=l-1;i<=mid;i++){
    		X[i]=i,Y[i]=F[i]+S[i]+(((LL)i*(LL)i+(LL)i)>>1);
    		while(top>1&&!conv(P[top-1],P[top],i)) top--; P[++top]=i;
    	}
    	for(int i=mid+1;i<=r;i++){
    		while(top>1&&Transl(P[top-1],i)>=Transl(P[top],i)) top--;
    		D[i]=Transl(P[top],i);
    	}
    	for(int i=r;i>mid;i--) D[i]=max(D[i+1],D[i]),H[i]=max(H[i],D[i]);
    	
    	for(int i=r;i>=l;i--) S[i]=S[i+1]+p[i]; top=0;
    	for(int i=r+1;i>mid;i--){
    		X[i]=i,Y[i]=G[i]+S[i]+(((LL)i*(LL)i+(LL)i)>>1);
    		while(top>1&&!conv(i,P[top],P[top-1])) top--; P[++top]=i;
    	}
    	for(int i=mid;i>=l;i--){
    		while(top>1&&Transr(P[top-1],i)>=Transr(P[top],i)) top--;
    		D[i]=Transr(P[top],i);
    	}
    	for(int i=l;i<=mid;i++) D[i]=max(D[i-1],D[i]),H[i]=max(H[i],D[i]);
    }
    int main(){
     	n=read(); for(int i=1;i<=n;i++) p[i]=read(),H[i]=-INF;
    	init(F),reverse(p+1,p+n+1),init(G),reverse(p+1,p+n+1);
    	reverse(G+1,G+n+1),G[0]=G[n+1],G[n+1]=0; solve(1,n);
    	for(int TT=read();TT;TT--,Putchar('
    ')){
    		LL pos=read(),K=read();
    		write(max(F[pos]+G[pos],H[pos]+p[pos]-K));
    	} flush(); return 0;
    }
  • 相关阅读:
    小球下落
    时隔半年再设环境变量。。笑哭了!
    Quartus+modelsim开发环境的搭建
    因式分解技巧——拆项与添项
    因式分解技巧——分组分解
    因式分解技巧——代公式
    因式分解技巧——提公因式
    一个2014年的数学日历
    用面积来证三角不等式
    一条直线若能平分矩形面积,则它必然经过矩形的中心
  • 原文地址:https://www.cnblogs.com/OYJason/p/9860345.html
Copyright © 2011-2022 走看看