[BZOJ 4826]影魔 区间修改主席树 标记永久化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ 4826]影魔 区间修改主席树 标记永久化相关的知识,希望对你有一定的参考价值。

为了这道题还特地去学了标记永久化,可能对于区间修改主席树或者树套树比较有用吧OvO

我们可以把答案分为两部分:p1造成的和p2造成的

我们枚举序列,用单调栈求出序列每一个位置i,左右边第一个比它大的L,R

开三棵主席树tree1 tree2 tree3

把L扔进tree1的R位置(单点+1),L+1~i-1扔进tree2的R位置,i+1~R-1扔进tree3的L位置(区间+1)

然后询问[l,r]的时候,求出三棵区间主席树

p1造成的贡献为区间tree1内大于等于L的个数

p2造成的贡献为区间tree2内大于等于L的个数和区间tree3内小于等于R的个数

因为要建主席树,所以我们先拿vector存一下每个位置需要往里扔什么,然后再扫一遍到一个点全扔进去(注意以这个点的最后一个版本为此位置的主席树)

还有要开long long!!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define pos2(i,a,b) for(int i=(a);i>=(b);i--)
#include<vector>
#define N 301000
#define LL long long
using namespace std;
int n,m,p1,p2;
int a[N];
int stack[N];
struct xixi{
	int l,r;
}cun[N];
vector<int> add1[N];
vector<xixi> add_l[N],add_r[N];
struct haha{
	int lc,rc,sum;
}tree1[N*25],tree_l[N*25],tree_r[N*25];
int size1,size_l,size_r;
int root1[N],root_l[N],root_r[N];
int biao_r[N*25],biao_l[N*25];
void update1(int &rt,int l,int r,int pos){
	tree1[++size1]=tree1[rt];
	int t=size1;tree1[t].sum++;rt=t;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(pos<=mid) update1(tree1[rt].lc,l,mid,pos);
	else update1(tree1[rt].rc,mid+1,r,pos);
	tree1[rt].sum=tree1[tree1[rt].lc].sum+tree1[tree1[rt].rc].sum;
}
void update_r(int old,int &rt,int l,int r,int xl,int xr){
	if(!rt) rt=++size_r;
	if(l>=xl&&r<=xr){
		biao_r[rt]=biao_r[old]+1;
		tree_r[rt].sum=tree_r[old].sum+(r-l+1);
		tree_r[rt].lc=tree_r[old].lc;
		tree_r[rt].rc=tree_r[old].rc;
		return;
	}
	tree_r[rt].sum=tree_r[old].sum+(xr-xl+1);
	int mid=(l+r)>>1;
	if(xr<=mid){
		update_r(tree_r[old].lc,tree_r[rt].lc,l,mid,xl,xr);
		tree_r[rt].rc=tree_r[old].rc;
	}
	else{
		if(xl>mid){
			update_r(tree_r[old].rc,tree_r[rt].rc,mid+1,r,xl,xr);
			tree_r[rt].lc=tree_r[old].lc;
		}
		else{
			update_r(tree_r[old].lc,tree_r[rt].lc,l,mid,xl,mid);
			update_r(tree_r[old].rc,tree_r[rt].rc,mid+1,r,mid+1,xr);
		}
	}
}
void update_l(int old,int &rt,int l,int r,int xl,int xr){
	if(!rt) rt=++size_l;
	if(l>=xl&&r<=xr){
		biao_l[rt]=biao_l[old]+1;
		tree_l[rt].sum=tree_l[old].sum+(r-l+1);
		tree_l[rt].lc=tree_l[old].lc;
		tree_l[rt].rc=tree_l[old].rc;
		return;
	}
	tree_l[rt].sum=tree_l[old].sum+(xr-xl+1);
	int mid=(l+r)>>1;
	if(xr<=mid){
		update_l(tree_l[old].lc,tree_l[rt].lc,l,mid,xl,xr);
		tree_l[rt].rc=tree_l[old].rc;
	}
	else{
		if(xl>mid){
			update_l(tree_l[old].rc,tree_l[rt].rc,mid+1,r,xl,xr);
			tree_l[rt].lc=tree_l[old].lc;
		}
		else{
			update_l(tree_l[old].lc,tree_l[rt].lc,l,mid,xl,mid);
			update_l(tree_l[old].rc,tree_l[rt].rc,mid+1,r,mid+1,xr);
		}
	}
}
int query1(int rtl,int rtr,int l,int r,int pos){
	if(!rtr) return 0;
	if(r<pos) return 0;
	if(l==r) return tree1[rtr].sum-tree1[rtl].sum;
	int mid=(l+r)>>1;
	if(pos<=mid){
		return query1(tree1[rtl].lc,tree1[rtr].lc,l,mid,pos)+tree1[tree1[rtr].rc].sum-tree1[tree1[rtl].rc].sum;	
	} 
	else return query1(tree1[rtl].rc,tree1[rtr].rc,mid+1,r,pos);
}
int query_r(int rtl,int rtr,int ad,int l,int r,int pos){
	 if(r<pos) return 0;
	 int tempad=biao_r[rtr]-biao_r[rtl];
	 if(l==r||l>=pos){
	 	return tree_r[rtr].sum-tree_r[rtl].sum+(ad)*(r-l+1);
	 }	
	 int mid=(l+r)>>1;
	 if(pos<=mid){
	 	return query_r(tree_r[rtl].lc,tree_r[rtr].lc,ad+tempad,l,mid,pos)+query_r(tree_r[rtl].rc,tree_r[rtr].rc,ad+tempad,mid+1,r,pos);
	 }
	 else return query_r(tree_r[rtl].rc,tree_r[rtr].rc,ad+tempad,mid+1,r,pos);
}
int query_l(int rtl,int rtr,int ad,int l,int r,int pos){
	 if(l>pos) return 0;
	 int tempad=biao_l[rtl]-biao_l[rtr];
	 if(l==r||r<=pos){
	 	return tree_l[rtl].sum-tree_l[rtr].sum+(ad)*(r-l+1);
	 }	
	 int mid=(l+r)>>1;
	 if(pos>mid){
	 	return query_l(tree_l[rtl].lc,tree_l[rtr].lc,ad+tempad,l,mid,pos)+query_l(tree_l[rtl].rc,tree_l[rtr].rc,ad+tempad,mid+1,r,pos);
	 }
	 else return query_l(tree_l[rtl].lc,tree_l[rtr].lc,ad+tempad,l,mid,pos);
}
int main(){
	scanf("%d%d%d%d",&n,&m,&p1,&p2);
	pos(i,1,n) scanf("%d",&a[i]);
	stack[0]=1;
	pos(i,1,n){
		while(stack[0]>1&&a[stack[stack[0]-1]]<a[i]) stack[0]--;
		if(stack[0]==1)	cun[i].l=0;
		else cun[i].l=stack[stack[0]-1];
		stack[stack[0]++]=i;
	}
	stack[0]=1;
	pos2(i,n,1){
		while(stack[0]>1&&a[stack[stack[0]-1]]<a[i]) stack[0]--;
		if(stack[0]==1)	cun[i].r=n+1;
		else cun[i].r=stack[stack[0]-1];
		stack[stack[0]++]=i;
	}
	pos(i,1,n){
		int l=cun[i].l,r=cun[i].r;
		if(l!=0) add1[r].push_back(l);
		if(i-1>=l+1&&r<=n) add_r[r].push_back((xixi){l+1,i-1});
		if(r-1>=i+1&&l>=1) add_l[l].push_back((xixi){i+1,r-1});
	}
	pos(i,1,n){
		int k;
		k=add1[i].size()-1;
		if(k<0)	root1[i]=root1[i-1];
		else{
			int t1=root1[i-1],t2;
			pos(j,0,k){
				t2=t1;
				update1(t2,1,n,add1[i][j]);
				t1=t2;
			}
			root1[i]=t1;
		}
		k=add_r[i].size()-1;
		if(k<0) root_r[i]=root_r[i-1];
		else{
			int t1=root_r[i-1],t2(0);
			pos(j,0,k){
				update_r(t1,t2,1,n,add_r[i][j].l,add_r[i][j].r);
				t1=t2;t2=0;
			}
			root_r[i]=t1;
		}	
	}
	pos2(i,n,1){
		int k=add_l[i].size()-1;
		if(k<0){
			root_l[i]=root_l[i+1];
		}
		else{
			int t1=root_l[i+1],t2(0);
			pos(j,0,k){
				update_l(t1,t2,1,n,add_l[i][j].l,add_l[i][j].r);
				t1=t2;t2=0;
			}
			root_l[i]=t1;
		}
	}
	LL ans(0);
	pos(i,1,m){
		ans=0;
		int x,y;scanf("%d%d",&x,&y);
		ans+=(query1(root1[x-1],root1[y],1,n,x)+(y-x))*1ll*p1;
		ans+=query_r(root_r[x-1],root_r[y],0,1,n,x)*1ll*p2;
		ans+=query_l(root_l[x],root_l[y+1],0,1,n,y)*1ll*p2;
		printf("%lld\\n",ans);
	}
	return 0;
}

  

以上是关于[BZOJ 4826]影魔 区间修改主席树 标记永久化的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 4826: [Hnoi2017]影魔 单调栈 主席树

[省选]省选知识点进度

[知识点]线段树标记永久化

AC日记——[Hnoi2017]影魔 bzoj 4826

bzoj 4826: [Hnoi2017]影魔单调栈+树状数组+扫描线

bzoj4826[hnoi2017]影魔