【BZOJ4867】【洛谷P5210】【ZJOI2017】—线段树(括号序列+树链剖分)

BZOJ

洛谷

LOJ


丢人O(nlog2n)O(nlog^2n)O(nlog2n)大常数跑不过BZOJBZOJBZOJ老爷机

考虑如果当前询问区间为[l,r][l,r][l,r]
我们从l−1l-1l1r+1r+1r+1的叶子节点往上走到lca(l−1,r+1)lca(l-1,r+1)lca(l1,r+1)
l−1l-1l1往上所有右儿子记下来,r+1r+1r+1所有左儿子记下来

那得到的区间就是要求的定位区间
但是每次定位显然也是不行的
考虑如何维护这样一堆儿子

以右儿子为例
考虑用类似括号序列的方法
dfsdfsdfs到某个点的时候,把(rc,1)(rc,1)(rc,1)加到序列里
dfsdfsdfs左儿子,再次回来的时候把(rc,−1)(rc,-1)(rc,1)加到序列里
dfsdfsdfs右儿子

可以发现这样l−1,lcal-1,lcal1,lca之间的路径就变成了序列上的一段区间
现在问题就变成了给定一个序列上的区间
询问区间所有点到某一个指定点的距离

dis(u,v)=dep(u)+dep(v)−2∗dep(lca)dis(u,v)=dep(u)+dep(v)-2*dep(lca)dis(u,v)=dep(u)+dep(v)2dep(lca)
两个depdepdep很好维护
我们考虑如何维护∑dep(lca)\sum dep(lca)dep(lca)
就是LcaLcaLca这道题

复杂度O(nlog2n)O(nlog^2n)O(nlog2n)大常数被O(nlogn)O(nlogn)O(nlogn)踩爆了

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0,f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
#define ll long long
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
const int N=800005;
struct ask{
	int l,coef,id;
};
vector<ask> q[2][N<<1];
vector<int> e[N];
int pos[2][N],dfn[2],in[2][N],val[2][N],*ps,*vl;
int dn;
int son[N][2],idx[N],rt,qry[N];
int n,m,tot;
ll ans[N];
int build(int l,int r){
	int u=++tot;
	if(l==r){idx[l]=u;return u;}
	int mid=read();
	son[u][0]=build(l,mid);
	son[u][1]=build(mid+1,r);
	e[u].pb(son[u][0]),e[u].pb(son[u][1]);
	return u;
}
void dfs1(int u){
	if(son[u][1])
		ps[++dfn[0]]=son[u][1],vl[dfn[0]]=1;
	in[0][u]=dfn[0];
	if(son[u][0])dfs1(son[u][0]);
	if(son[u][1])
		ps[++dfn[0]]=son[u][1],vl[dfn[0]]=-1,dfs1(son[u][1]);
}
void dfs2(int u){
	if(son[u][0])
		ps[++dfn[1]]=son[u][0],vl[dfn[1]]=1;
	in[1][u]=dfn[1];
	if(son[u][1])dfs2(son[u][1]);
	if(son[u][0])
		ps[++dfn[1]]=son[u][0],vl[dfn[1]]=-1,dfs2(son[u][0]);
}
namespace Tr{
	ll tr[N<<2];int tag[N<<2];
	#define lc (u<<1)
	#define rc ((u<<1)|1)
	#define mid ((l+r)>>1)
	inline void pushup(int u){
		tr[u]=tr[lc]+tr[rc];
	}
	inline void pushdown(int u,int l,int r){
		if(!tag[u])return ;
		tag[lc]+=tag[u],tag[rc]+=tag[u];
		tr[lc]+=1ll*(mid-l+1)*tag[u],tr[rc]+=1ll*(r-mid)*tag[u];
		tag[u]=0;
	}
	void update(int u,int l,int r,int st,int des,int k){
		if(st<=l&&r<=des){tag[u]+=k,tr[u]+=1ll*(r-l+1)*k;return;}
		pushdown(u,l,r);
		if(st<=mid)update(lc,l,mid,st,des,k);
		if(mid<des)update(rc,mid+1,r,st,des,k);
		pushup(u);
	}
	ll query(int u,int l,int r,int st,int des){
		if(st<=l&&r<=des){return tr[u];}
		pushdown(u,l,r);ll res=0;
		if(st<=mid)res+=query(lc,l,mid,st,des);
		if(mid<des)res+=query(rc,mid+1,r,st,des);
		pushup(u);return res;
	}
	inline void clear(){
		memset(tr,0,sizeof(tr)),memset(tag,0,sizeof(tag));
	}
	int In[N],dfn,siz[N],hson[N],top[N],dep[N],fa[N];
	void dfs1(int u){
		siz[u]=1;
		for(auto &v:e[u]){
			fa[v]=u,dep[v]=dep[u]+1;
			dfs1(v),siz[u]+=siz[v];
			if(siz[v]>siz[hson[u]])hson[u]=v;
		}
	}
	void dfs2(int u,int tp){
		In[u]=++dfn,top[u]=tp;
		if(hson[u])dfs2(hson[u],tp);
		for(auto &v:e[u]){
			if(v==hson[u])continue;
			dfs2(v,v);
		}
	}
	inline void init(){
		dep[rt]=1,dfs1(rt),dfs2(rt,rt);
	}
	inline void pathupdate(int u,int v,int k){
		while(top[u]!=top[v]){
			if(dep[top[u]]<dep[top[v]])swap(u,v);
			update(1,1,dfn,In[top[u]],In[u],k);
			u=fa[top[u]];
		}
		if(dep[u]<dep[v])swap(u,v);
		update(1,1,dfn,In[v],In[u],k);
	}
	inline ll pathquery(int u,int v){
		ll res=0;
		while(top[u]!=top[v]){
			if(dep[top[u]]<dep[top[v]])swap(u,v);
			res+=query(1,1,dfn,In[top[u]],In[u]);
			u=fa[top[u]];
		}
		if(dep[u]<dep[v])swap(u,v);
		res+=query(1,1,dfn,In[v],In[u]);
		return res;
	}
	inline int Lca(int u,int v){
		while(top[u]!=top[v]){
			if(dep[top[u]]<dep[top[v]])swap(u,v);
			u=fa[top[u]];
		}
		return dep[u]<dep[v]?u:v;
	}
}
inline void query(int u,int l,int r,int id){
	l=idx[l-1],r=idx[r+1];
	int lca=Tr::Lca(l,r);
	q[0][in[0][l]].pb(ask{u,1,id});
	q[0][in[0][lca]].pb(ask{u,-1,id});
	q[1][in[1][r]].pb(ask{u,1,id});
	q[1][in[1][lca]].pb(ask{u,-1,id});
}
inline void calc(int ID,int *pos,int dfn,int *val,int *in){
	ll sum0=0,sum1=0;
	for(int i=1;i<=dfn;i++){
		sum0+=val[i],sum1+=Tr::dep[pos[i]]*val[i];
		Tr::pathupdate(rt,pos[i],val[i]);
		for(auto &x:q[ID][i]){
			ll res=sum0*Tr::dep[x.l];
			res+=sum1,res-=2ll*Tr::pathquery(x.l,rt);
			ans[x.id]+=res*x.coef;
		}
	}
	Tr::clear();
}
signed main(){
	n=read();
	build(1,n);
	idx[0]=++tot,idx[n+1]=++tot;
	rt=++tot;son[rt][0]=idx[0],son[rt][1]=++tot;
	e[rt].pb(idx[0]),e[rt].pb(tot);
	son[tot][0]=1,son[tot][1]=idx[n+1];
	e[tot].pb(1),e[tot].pb(idx[n+1]);
	Tr::init();
	ps=pos[0],vl=val[0];
	dfs1(rt),ps=pos[1],vl=val[1];
	dfs2(rt),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),l=read(),r=read();
		query(u,l,r,i);
	}
	calc(0,pos[0],dfn[0],val[0],in[0]),calc(1,pos[1],dfn[1],val[1],in[1]);
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';exit(0);
}

转载于:https://www.cnblogs.com/stargazer-cyk/p/11145537.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值