CF1495F Squares

CF1495F Squares

感觉很棒的一道题,自主杀的第一道\(3300\)
显然,对于\(A\)操作,构成的图为一条链;对于\(B\)操作,构成的图为一颗树。(瞬间想到PKUSC2021D1T2)
问题的核心在于权衡\(AB\)之间的操作关系.

对于这类"右边第一个比他大的"的问题,有一个通用的性质,设\(i\)右侧第一个满足\(p_j\)>\(p_i\)的点为\(to_i\):

\[\forall j\in [i,to_i-1],p_{to_j}\geq p_{to_i} \]

也就是说相邻的两点要么是父子关系,要么有共同的祖先\(to_i\)
也就是说无论怎么决策\(i\)在向右跳的过程中必经过\(to_i\)

由于点的跳跃单调向后,不妨考虑倒序决策,设当前决策的点为\(i\)
由于无论怎么决策必经过\(to_i\),所以不妨设出\(val\)数组,\(val_i\)表示\(i\)跳到\(to_i\)所需的最小贡献
在已知\(val[i+1, \dots n]\)的情况下,决策就变得非常清新。
要么\(B\)操作,直接到\(to_i\);要么\(A\)操作,然后记录由\(i+1\)跳到\(to_i\)
由于\(i+1\)也为\(to_i\)则子孙(或本身),所以可以记录\(i+1\)\(to_i\)树径上点的val和,这可以使用倍增来维护。

有了上述结论,我们就可以在\(log n\)的时间内计算出存在父子关系两点间的花费了
然后考虑将其拓展到任意两点间的花费,用函数DIS(a,b)表示
先按照已有的方法做,先让\(a\)爬树,然后需要一次\(A\)操作使其跳到另一颗子树内继续爬树
显然这个复杂度使不稳定的。
但是在a爬完一次数后,剩下的操作简化爬树过程后实际上是在原序列的单调不上升序列上跳跃,预处理即可

剩下的可以采用任意一种数据结构,维护区间信息,在合并时只需要计算左区间的最右点和右区间的最左点之间的贡献
因为\(S\)集合中的点都必须经过,点又是单调递增的,所以两区间显然是独立的。

此题结束,疑问见代码

#include<bits/stdc++.h>
using namespace std;

#define Mod(x) (x>=P)&&(x-=P)
#define debug(x) cerr<<#x<<"="<<x<<endl
#define rep(i,a,b) for(ll i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(ll i=a,i##end=b;i>=i##end;--i)
#define erep(i,a,b) for(ll i=hd[a];i;i=nxt[i])

typedef long long ll;
void Max(ll &x,int y){x<y&&(x=y);}
void Min(ll &x,int y){x>y&&(x=y);}

bool vio;
char IO;
ll rd(int res=0){
	bool f=0;
	while(IO=getchar(),IO<48||IO>57)
		f|=IO=='-';
	do res=(res<<1)+(res<<3)+(IO^48);
	while(IO=getchar(),isdigit(IO));
	return f?-res:res;
}
const ll M=2e5+10;
ll L[M],R[M],fa[22][M],val[22][M];
ll A[M],p[M],B[M],pA[M],stk[M],top;
ll n,q,pre[M],ldis[M];
vector<int>G[M];
bool mk[M];
void dfs(ll x){
	static ll tot;
	L[x]=++tot;
	for(ll y:G[x]){
		fa[0][y]=x;
		rep(i,1,20)fa[i][y]=fa[i-1][fa[i-1][y]];
		dfs(y);
	}
	R[x]=tot;
}	
bool FA(int a,int b){
	return L[a]<=L[b]&&L[b]<=R[a];
}
ll DIS(int a,int b){
	ll res=0;
	drep(i,20,0)if(fa[i][a]&&fa[i][a]<=b)
		res+=val[i][a],a=fa[i][a];
	return res+ldis[b]-ldis[a];
}
struct Seg{
	struct node{
		ll ls,rs,res;
		node operator +(const node &_)const{
			node res;
			res.res=(*this).res+_.res;
			res.ls=ls?ls:_.ls;
			res.rs=_.rs?_.rs:rs;
			if(rs&&_.ls)res.res+=DIS(rs,_.ls);
			return res;
		}
	}t[M<<2];
	void upd(ll x,int L=1,int R=n+1,int p=1){
		if(L==R){
			if(!mk[L]){
				mk[L]=1;
				t[p]=(node){L,L,0};
			}else{
				mk[L]=0;
				t[p]=(node){0,0,0};
			}
			return ;
		}
		ll mid=(L+R)>>1;
		if(x<=mid)upd(x,L,mid,p<<1);
		else upd(x,mid+1,R,p<<1|1);
		t[p]=t[p<<1]+t[p<<1|1];
	}
}Tr;
bool let;
int main(){
	cerr<<(&vio-&let)/1024.0/1024<<endl;
	n=rd(),q=rd();	
	p[n+1]=1e9;
	rep(i,1,n)p[i]=rd();
	rep(i,1,n)A[i]=rd(),pA[i]=pA[i-1]+A[i];
	rep(i,1,n)B[i]=rd();
	stk[++top]=n+1;
	drep(i,n,1){
		while(p[stk[top]]<=p[i])top--;
		G[stk[top]].push_back(i);
		stk[++top]=i;
	}
	dfs(n+1);
	drep(i,n,1){
		ll t=i+1,res=A[i];
		drep(j,20,0)if(FA(fa[0][i],fa[j][t]))
			res+=val[j][t],t=fa[j][t];
		val[0][i]=min(res,B[i]);
		rep(j,1,20)val[j][i]=val[j-1][i]+val[j-1][fa[j-1][i]];
	}
	top=0;
	rep(i,1,n){
		while(top&&p[stk[top]]<p[i])top--;
		if(top)ldis[i]=ldis[stk[top]]+DIS(stk[top]+1,i)+A[stk[top]];
		stk[++top]=i;
	}
	Tr.upd(1),Tr.upd(n+1);
	while(q--){
		ll x=rd();
		if(x!=1)Tr.upd(x);
		printf("%lld\n",Tr.t[1].res);
	}
	return 0;
}
posted @ 2021-08-09 09:49  Saltywater  阅读(59)  评论(1)    收藏  举报