[PA2017]Banany

XVIII.[PA2017]Banany

心血来潮想开道动态点分治的题练手,然后被折磨了一下午……

首先,套上点分树是没问题的。那么,怎样维护修改呢?

单点修改无论用什么结构维护都是非常easy的;但是边的修改就不太简单了,因为它涉及到不止一条路径。

我们设当前修改了边 \((x,y)\)。对于点分树上的点,若其在原树上的 \(x\) 一侧,显然其子树中 \(y\) 一侧的路径要变化;若其在原树上 \(y\) 一侧,则 \(x\) 一侧的路径要变化。

考虑所有分治块横跨 \((x,y)\) 边的点,发现其仅有可能是 \(x,y\) 之一的祖先。于是我们只需处理 \(x,y\) 祖先的并集。

某侧的路径要变化,在原树上是子树。于是我们便知道了应该用何种结构维护修改:套在原树dfs序上的线段树。这样子,\(x\) 侧点就修改 \(y\) 侧路径(在原树dfs序上是一段区间),\(y\) 侧点就修改 \(x\) 侧路径(也是一段区间),便能做到单次修改 \(O(\log n)\)

但是我们似乎忘记还要挖掉子节点的分治树的答案。但是仔细一想,如果算了子节点的答案,就等价于路径是到了当前节点再折返,答案一定更劣(因为边权为正),所以不用考虑挖不挖子节点的答案。

于是移动就直接用线段树的答案和两点间距离拼出来即可。注意这里两点间距离应是实际距离,会随着边权修改而变化,因此要专门开一棵BIT维护!

细节大概就这么多。时间复杂度 \(O(n\log^2n)\)。线段树不要写动态开点,直接用 vector 维护即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
typedef long long ll;
int n,m;
ll a[100100];
namespace CDT{
	struct dat{
		ll val;int id;
		dat(){val=0xc0c0c0c0c0c0c0c0,id=0;}
		dat(ll V,int I){val=V,id=I;}
		friend bool operator<(const dat&u,const dat&v){return u.val!=v.val?u.val<v.val:u.id>v.id;}
		void operator+=(const ll&v){val+=v;}
		friend dat operator-(const dat&u,const ll&v){return dat(u.val-v,u.id);}
	}; 
	struct SegTree{dat mx;ll tag;void operator+=(const ll&v){mx+=v,tag+=v;}};
	struct ST:vector<SegTree>{
		#define lson x<<1|1
		#define rson (x+1)<<1
		#define mid ((l+r)>>1)
		void pushup(int x){(*this)[x].mx=max((*this)[lson].mx,(*this)[rson].mx);}
		void pushdown(int x){ll&v=(*this)[x].tag;(*this)[lson]+=v,(*this)[rson]+=v,v=0;}
		void modify(int x,int l,int r,int L,int R,ll val){
			if(l>R||r<L)return;
			if(L<=l&&r<=R){(*this)[x]+=val;return;}
			pushdown(x),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),pushup(x);
		}
		void modify(int x,int l,int r,int P,dat val){
			if(l>P||r<P)return;
			if(l==r){(*this)[x].mx=val;return;}
			pushdown(x),modify(lson,l,mid,P,val),modify(rson,mid+1,r,P,val),pushup(x);
		}
		dat query(int x,int l,int r,int ban){
			if((*this)[x].mx.id!=ban)return (*this)[x].mx;
			if(l==r)return dat();
			pushdown(x);return max(query(lson,l,mid,ban),query(rson,mid+1,r,ban));
		}
		int build(int x,int l,int r){if(l==r)return x;return max(build(lson,l,mid),build(rson,mid+1,r));}
		void iterate(int x,int l,int r){printf("%d:[%d,%d]:%lld,%d\n",x,l,r,(*this)[x].mx.val,(*this)[x].mx.id);if(l!=r)pushdown(x),iterate(lson,l,mid),iterate(rson,mid+1,r);}
	}seg[100100];
	int RT;
	vector<int>v[100100];
	int fa[100100];
}
namespace TRE{
	int head[100100],cnt,sz[100100],msz[100100],RT,SZ;
	struct node{int to,next;ll val;}edge[201000];
	il void ae(rg int u,rg int v,rg ll w){
		edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
		edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=w,head[v]=cnt++;
	}
	bool vis[100100];
	il void getroot(rg int x,rg int fa){
		sz[x]=1,msz[x]=0;
		for(rg int i=head[x],y;i!=-1;i=edge[i].next)if((y=edge[i].to)!=fa&&!vis[y])getroot(y,x),sz[x]+=sz[y],msz[x]=max(msz[x],sz[y]);
		msz[x]=max(msz[x],SZ-sz[x]);
		if(msz[x]<msz[RT])RT=x;
	}
	il void getsz(rg int x,rg int fa){sz[x]=1;for(rg int i=head[x],y;i!=-1;i=edge[i].next)if((y=edge[i].to)!=fa&&!vis[y])getsz(y,x),sz[x]+=sz[y];}
	il void solve(rg int x){
		getsz(x,0),vis[x]=true;
		for(rg int i=head[x],y;i!=-1;i=edge[i].next)if(!vis[y=edge[i].to])RT=0,SZ=sz[y],getroot(y,0),CDT::fa[RT]=x,solve(RT);
	}
	int dep[100100],st[200100][20],tot,LG[200100],fir[100100],dfn[100100],nfd;
	ll dis[100100],val[100100];
	il void dfs(int x,int fa){
		st[++tot][0]=x,fir[x]=tot,dfn[x]=++nfd,sz[x]=1;
		for(rg int i=head[x],y;i!=-1;i=edge[i].next)if((y=edge[i].to)!=fa)dis[y]=dis[x]+(val[y]=edge[i].val),dep[y]=dep[x]+1,dfs(y,x),st[++tot][0]=x,sz[x]+=sz[y];
	}
	il int MIN(rg int x,rg int y){return dep[x]<dep[y]?x:y;}
	il int LCA(rg int x,rg int y){x=fir[x],y=fir[y];if(x>y)swap(x,y);int k=LG[y-x+1];return MIN(st[x][k],st[y-(1<<k)+1][k]);}
	il ll DIS(rg int x,rg int y){return dis[x]+dis[y]-2*dis[LCA(x,y)];}
	il void init(){
		memset(head,-1,sizeof(head));for(rg int i=1;i<n;i++){int x,y;ll z;scanf("%d%d%lld",&x,&y,&z),ae(x,y,z);}
		msz[RT=0]=SZ=n,getroot(1,0),solve(CDT::RT=RT);
		dfs(1,0);for(rg int i=2;i<=tot;i++)LG[i]=LG[i>>1]+1;
		for(rg int j=1;j<=LG[tot];j++)for(rg int i=1;i+(1<<j)-1<=tot;i++)st[i][j]=MIN(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	}
	ll t[100100];
	void ADD(int x,ll y){while(x<=n)t[x]+=y,x+=x&-x;}
	ll SUM(int x){ll ret=0;while(x)ret+=t[x],x-=x&-x;return ret;}
	ll DS(int x){return dis[x]+SUM(dfn[x]);}
	ll D(int x,int y){return DS(x)+DS(y)-2*DS(LCA(x,y));}
}
using TRE::DIS;
using TRE::dfn;
using TRE::sz;
using TRE::D;
namespace CDT{
	#define all(x) x.begin(),x.end()
	int P=1;
	void init(){
		for(int i=1;i<=n;i++)for(int j=i;j;j=fa[j])v[j].push_back(dfn[i]);
//		for(int i=1;i<=n;i++){for(auto j:v[i])printf("%d ",j);puts("");}
		for(int i=1;i<=n;i++)sort(all(v[i])),seg[i].resize(seg[i].build(0,0,v[i].size()-1)+1);
//		for(int i=1;i<=n;i++)printf("%d %d\n",seg[i].size(),v[i].size()),seg[i].iterate(0,0,v[i].size()-1),puts("");
		for(int i=1;i<=n;i++)for(int j=i;j;j=fa[j])seg[j].modify(0,0,v[j].size()-1,lower_bound(all(v[j]),dfn[i])-v[j].begin(),dat(a[i]-DIS(i,j),i));
	}
	void modifypoint(int x,ll val){val-=a[x],a[x]+=val;for(int y=x,p;y;y=fa[y])p=lower_bound(all(v[y]),dfn[x])-v[y].begin(),seg[y].modify(0,0,v[y].size()-1,p,p,val);}
	bool sp[100100];
	void IN(int z,int x,ll val){
		seg[z].modify(0,0,v[z].size()-1,0,(lower_bound(all(v[z]),dfn[x])-v[z].begin())-1,-val);
		seg[z].modify(0,0,v[z].size()-1,upper_bound(all(v[z]),dfn[x]+sz[x]-1)-v[z].begin(),v[z].size()-1,-val);		
	}
	void OUT(int z,int x,ll val){
		seg[z].modify(0,0,v[z].size()-1,lower_bound(all(v[z]),dfn[x])-v[z].begin(),(upper_bound(all(v[z]),dfn[x]+sz[x]-1)-v[z].begin())-1,-val);
	}
	void modifyedge(int x,int y,ll val){
		if(TRE::dep[x]<TRE::dep[y])swap(x,y);
//		printf("[%d,%d,%d]\n",x,TRE::val[x],val);
		val-=TRE::val[x],TRE::val[x]+=val;
		TRE::ADD(dfn[x],val),TRE::ADD(dfn[x]+sz[x],-val);
		for(int z=x;z;sp[z]=true,z=fa[z])if(dfn[z]>=dfn[x]+sz[x]||dfn[z]<dfn[x])OUT(z,x,val);else IN(z,x,val);
		for(int z=y;z;z=fa[z]){
			if(sp[z])continue;
			if(dfn[z]>=dfn[x]+sz[x]||dfn[z]<dfn[x])OUT(z,x,val);else IN(z,x,val);
		}
		for(int z=x;z;sp[z]=false,z=fa[z]);
	}
	void query(){dat mx;for(int z=P;z;z=fa[z])mx=max(mx,seg[z].query(0,0,v[z].size()-1,P)-D(P,z));P=mx.id;}
}
int main(){
	scanf("%d%d",&n,&m);
	for(rg int i=1;i<=n;i++)scanf("%lld",&a[i]);
	TRE::init(),CDT::init();
	for(int i=1,tp;i<=m;i++){
		int x,y;ll z;scanf("%d",&tp);
		if(tp==1)scanf("%d%lld",&x,&z),CDT::modifypoint(x,z);else scanf("%d%d%lld",&x,&y,&z),CDT::modifyedge(x,y,z);
//		for(int j=1;j<=n;j++)CDT::seg[j].iterate(0,0,CDT::v[j].size()-1),puts("");
		CDT::query();printf("%d ",CDT::P);
	}
	return 0;
} 
原文地址:https://www.cnblogs.com/Troverld/p/14607797.html