【LOJ6184】无心行挽(虚树)(树形DP)(树链剖分)(ST表)

传送门


题解:

虚树好题,后续的处理其实也可以用倍增,不过树剖常数那么小直接上树剖+ST表完全没有压力。

首先看到题目这个形式大概就是要用虚树的了。

求的是距离所有关键点的最小值的最大值。

答案点有三种情况,在虚树节点上,在虚树缩掉的边的子树中,在虚树上的点的无关键点子树中。

第一种树形DP,第二种树剖+DFS序+ST表,第三种BFS序+ST表。

非常好写

写完了才发现第三种其实也可以用DFS序


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=1e5+7;

int n,m;

int el[N],nxt[N<<1|1],to[N<<1|1],ec;
inline void adde(int u,int v){
	nxt[++ec]=el[u],el[u]=ec,to[ec]=v;
	nxt[++ec]=el[v],el[v]=ec,to[ec]=u;
}

int Log[N];
class ST{
	private:
		int st[20][N];
	public:
		void init(int n){
			for(int re i=1;i<=Log[n];++i)
			for(int re j=1;j+(1<<i)-1<=n;++j)
			st[i][j]=std::max(st[i-1][j],st[i-1][j+(1<<i-1)]);
		}
		void set(int p,int v){st[0][p]=v;}
		int operator[](int o)cs{return st[0][o];}
		int operator()(int l,int r)cs{int t=Log[r-l+1];
			return std::max(st[t][l],st[t][r-(1<<t)+1]);
		}
}up,dn,sub;

int fa[N],son[N],siz[N];
int d[N],top[N];
int dfn[N],nd[N],dfc;
int bfn[N],L[N],R[N],bfc;
int f[N],g[N],s[N];

void dfs1(int u,int p){
	fa[u]=p,d[u]=d[p]+1;siz[u]=1;
	for(int re e=el[u],v;e;e=nxt[e])
	if((v=to[e])!=p){
		dfs1(v,u);siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
		if(1+f[v]>f[u])g[u]=f[u],f[u]=1+f[v];
		else if(1+f[v]>g[u])g[u]=1+f[v];
	}
}
void dfs2(int u,int tp){
	top[u]=tp,dfn[u]=++dfc,nd[dfc]=u;
	for(int re e=el[u],v;e;e=nxt[e])
	if((v=to[e])!=fa[u])s[v]=1+std::max(s[u],1+f[v]==f[u]?g[u]:f[u]);
	if(son[u])dfs2(son[u],tp);
	for(int re e=el[u],v;e;e=nxt[e])
	if((v=to[e])!=fa[u]&&v!=son[u])dfs2(v,v);
	int mx=1+f[son[u]]==f[u]?g[u]:f[u];
	up.set(dfn[u],mx-d[u]);
	dn.set(dfn[u],mx+d[u]);
}
std::queue<int> q;
void bfs(){
	q.push(1);
	while(!q.empty()){
		int u=q.front();
		q.pop();bfn[u]=++bfc;
		sub.set(bfn[u],1+f[u]);
		if(fa[u]){
			R[fa[u]]=bfn[u];
			if(!L[fa[u]])L[fa[u]]=bfn[u];
		}
		for(int re e=el[u],v;e;e=nxt[e])
		if((v=to[e])!=fa[u])q.push(v);
	}
}
int LCA(int u,int v){
	while(top[u]!=top[v])d[top[u]]<d[top[v]]?v=fa[top[v]]:u=fa[top[u]];
	return d[u]<d[v]?u:v;
}
int jump(int u,int k){
	while(k>d[u]-d[top[u]]){
		k-=d[u]-d[top[u]]+1;
		u=fa[top[u]];
	}return nd[dfn[u]-k];
}
int find_to(int u,int p){int res=0;
	while(top[u]!=top[p]){
		res=top[u];
		u=fa[top[u]];
	}return u==p?res:son[p];
}
int qy(int u,int v,ST &T,int coe){
	int ans=-1e9;
	while(d[u]>d[v]){
		int p=fa[u];
		if(u==son[p])ans=std::max(ans,T(std::max(dfn[top[p]],dfn[v]),dfn[p]));
		else {
			ans=std::max(ans,coe*d[p]+(1+f[u]==f[p]?g[p]:f[p]));
			if(p!=top[p])ans=std::max(ans,T(std::max(dfn[top[p]],dfn[v]),dfn[fa[p]]));
		}u=top[p];
	}
	return ans;
}
namespace VT{
	int el[N],nxt[N],key[N];
	int dis[N],from[N];
	int vt[N],k,rt,ans;
	int st[N],tp;
	inline void adde(int u,int v){if(u==v)return ;nxt[v]=el[u],el[u]=v;}
	void dp1(int u){
		if(key[u])dis[u]=0,from[u]=u;
		else dis[u]=1e9,from[u]=0;
		for(int re v=el[u];v;v=nxt[v]){
			dp1(v);if(d[v]-d[u]+dis[v]<dis[u]){
				dis[u]=dis[v]-d[u]+d[v];
				from[u]=from[v];
			}
		}
	}
	void dp2(int u){
		for(int re v=el[u];v;v=nxt[v]){
			if(d[v]-d[u]+dis[u]<dis[v]){
				dis[v]=dis[u]+d[v]-d[u];
				from[v]=from[u];
			}dp2(v);
		}
	}
	void sol(int u){
		tp=0;
		for(int re v=el[u];v;v=nxt[v]){
			int c=find_to(v,u);st[++tp]=bfn[c];
			if(from[u]!=from[v]){
				int dd=(dis[u]+dis[v]+d[v]-d[u])/2-dis[v];
				int t=jump(v,(dd==d[v]-d[u])?dd-1:dd);
				ans=std::max(ans,dis[v]+d[v]+qy(v,t,up,-1));
				ans=std::max(ans,dis[u]-d[u]+qy(t,c,dn,1));
			}else {
				if(dis[u]<dis[v])ans=std::max(ans,dis[u]-d[u]+qy(v,c,dn,1));
				else ans=std::max(ans,dis[v]+d[v]+qy(v,c,up,-1));
			}
		}
		if(R[u]){
			std::sort(st+1,st+tp+1);
			int res=0,lst=L[u];
			for(int re i=1;i<=tp;++i){
				if(st[i]>lst)res=std::max(res,sub(lst,st[i]-1));
				lst=st[i]+1;
			}if(R[u]>=lst)res=std::max(res,sub(lst,R[u]));
			ans=std::max(ans,res+dis[u]);
		}
		for(int re v=el[u];v;v=nxt[v])sol(v);
		
	}
	void work(){
		ec=0;k=gi();for(int re i=1;i<=k;++i)vt[i]=gi();
		std::sort(vt+1,vt+k+1,[](int u,int v){return dfn[u]<dfn[v];});
		for(int re i=2;i<=k;++i){
			int p=LCA(vt[i-1],vt[i]);
			el[p]=0,key[p]=false;
		}
		for(int re i=1;i<=k;++i)el[vt[i]]=0,key[vt[i]]=true;
		rt=st[tp=1]=LCA(vt[1],vt[k]);
		for(int re i=1;i<=k;++i){
			int p=LCA(vt[i],st[tp]);
			while(tp>1&&d[st[tp-1]]>=d[p])
			adde(st[tp-1],st[tp]),--tp;
			if(d[st[tp]]>=d[p])adde(p,st[tp]),st[tp]=p;
			st[++tp]=vt[i];
		}
		while(tp>1)adde(st[tp-1],st[tp]),--tp;
		dp1(rt);dp2(rt);ans=dis[rt]+s[rt];
		sol(rt);cout<<ans<<"\n";
	}
}

signed main(){
#ifdef zxyoi
	freopen("nothing_to_do.in","r",stdin);
#endif
	n=gi();m=gi();
	for(int re i=1;i<n;++i)adde(gi(),gi());
	for(int re i=2;i<=n;++i)Log[i]=Log[i>>1]+1;
	dfs1(1,0);dfs2(1,1);bfs();
	up.init(n),dn.init(n),sub.init(n);
	memset(el,0,sizeof el);while(m--)VT::work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值