cunzai_zsy0531

关注我

P4565 [CTSC2018]暴力写挂 题解

题面

这题感觉思路比较正常。首先考虑化式子,原式需要在两棵树上分别求 \(lca\),这很不好。考虑把式子写成

\[\frac{1}{2}\big(dis_1(x,y)+dep_1(x)+dep_1(y)-2\times dep_2(LCA_2(x,y)\big) \]

对于这个 \(dis_1\),很显然的做法就是在第一棵树上点分治。考虑当前连通块,每个点维护一个权值 \(w_u=dep_1(u)+dis_1(u,rt)\),其中 \(rt\) 是当前连通块的重心。把这些点在第二棵树上建虚树,在虚树上 dp。由于不能统计第一棵树同一子树中的点对,所以需要维护一个最大值,一个次大值,并且保证这两个值不在同一子树中。合并的时候顺便统计答案即可。复杂度 \(2\log\)

注意一些细节的处理,比如 \(1\) 号点也在虚树上,比如 \(x=y\) 的情况等等。

点击查看代码
inline void clearvector(std::vector<int> &a){std::vector<int> b;std::swap(a,b);}
const int N=3.7e5+13;
struct Edge{int v,w,nxt;};
int n;
ll ans=-LLINF;

namespace tree2{
struct Stack{
	int s[N],t;
	inline void clear(){s[t=0]=0;}
	Stack(){clear();}
	inline void push(int x){s[++t]=x;}
	inline void pop(){--t;}
	inline int ttop(){return s[t-1];}
	inline int top(){return s[t];}
	inline bool empty(){return !t;}
};
Edge e[N<<1];
int h[N],etot;
inline void add_edge(int u,int v,int w){e[++etot]=(Edge){v,w,h[u]};h[u]=etot;}
inline void readedge(){
	for(int i=1;i<n;++i){
		int u,v,w;read(u),read(v),read(w);
		add_edge(u,v,w),add_edge(v,u,w);
	}
}
int fa[N],siz[N],dep[N],son[N],top[N],id[N],dfs_clock;
ll dis[N];
void dfs1(int u,int f,int deep){
	fa[u]=f,siz[u]=1,dep[u]=deep;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==f) continue;
		dis[v]=dis[u]+e[i].w;
		dfs1(v,u,deep+1);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int topf){
	top[u]=topf,id[u]=++dfs_clock;
	if(!son[u]) return;
	dfs2(son[u],topf);
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v!=son[u]&&v!=fa[u]) dfs2(v,v);
	}
}
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 id[u]<id[v]?u:v;
}
inline void init(){dfs1(1,0,0);dfs2(1,1);}
std::vector<int> G[N],fuck;
bool vis[N];
inline bool cmp(const int &x,const int &y){return id[x]<id[y];}
inline void Add(int u,int v){G[u].pb(v),fuck.pb(u),fuck.pb(v);}
inline void build(std::vector<int> a){
	Stack st;clearvector(fuck);
	std::sort(a.begin(),a.end(),cmp);
	st.push(1);
	for(int i=0,lim=a.size();i<lim;++i){
		vis[a[i]]=1;if(a[i]==1) continue;
		int t=lca(a[i],st.top());
		if(t!=st.top()){
			while(st.t>1&&id[t]<id[st.ttop()]) Add(st.ttop(),st.top()),st.pop();
			if(t!=st.ttop()) Add(t,st.top()),st.pop(),st.push(t);
			else Add(t,st.top()),st.pop();
		}
		st.push(a[i]);
	}
	for(int i=1;i<st.t;++i) Add(st.s[i],st.s[i+1]);
}
inline void clear(){
	for(auto x:fuck) clearvector(G[x]),vis[x]=0;
}
void dp(int u);
}

namespace tree1{
Edge e[N<<1];
int h[N],etot;
inline void add_edge(int u,int v,int w){e[++etot]=(Edge){v,w,h[u]};h[u]=etot;}
inline void readedge(){
	for(int i=1;i<n;++i){
		int u,v,w;read(u),read(v),read(w);
		add_edge(u,v,w),add_edge(v,u,w);
	}
}
int maxx[N],siz[N],rt,psum,top[N];
bool vis[N];
std::vector<int> point;
ll d[N],w[N];
void init(int u,int f){
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==f)continue;
		d[v]=d[u]+e[i].w;
		init(v,u);
	}
}
void findrt(int u,int f){
	siz[u]=1,maxx[u]=0;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==f||vis[v]) continue;
		findrt(v,u);
		siz[u]+=siz[v];
		maxx[u]=max(maxx[u],siz[v]);
	}
	maxx[u]=max(maxx[u],psum-siz[u]);
	if(maxx[u]<maxx[rt]) rt=u;
}
void dfs(int u,int f,ll dis,int topf){
	point.pb(u);w[u]=dis+d[u];top[u]=topf;
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v!=f&&!vis[v]) dfs(v,u,dis+e[i].w,topf);
	}
}
void solve(int u){
	vis[u]=1;
	clearvector(point);point.pb(u),top[u]=u,w[u]=d[u];
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(vis[v]) continue;
		dfs(v,u,e[i].w,v);
	}
	tree2::build(point);
	tree2::dp(1);
	tree2::clear();
	for(int i=h[u];i;i=e[i].nxt){
		int v=e[i].v;if(vis[v]) continue;
		rt=0,psum=siz[v];
		findrt(v,0),findrt(rt,0);
		solve(rt);
	}
}
inline void mian(){
	init(1,0);
	maxx[rt=0]=INF,psum=n;
	findrt(1,0),findrt(rt,0);
	solve(rt);
}
}

namespace tree2{
ll f[N][2];int g[N][2];
void dp(int u){
	f[u][0]=(vis[u]?tree1::w[u]:-LLINF),f[u][1]=-LLINF;
	g[u][0]=(vis[u]?tree1::top[u]:0),g[u][1]=0;
	for(auto v:G[u]){
		dp(v);
		if(vis[u]){
			if(g[u][0]!=g[v][0]) ans=max(ans,(f[u][0]+f[v][0]-2*dis[u])/2);
			if(g[u][0]!=g[v][1]) ans=max(ans,(f[u][0]+f[v][1]-2*dis[u])/2);
			if(g[u][1]!=g[v][0]) ans=max(ans,(f[u][1]+f[v][0]-2*dis[u])/2);
			if(g[u][1]!=g[v][1]) ans=max(ans,(f[u][1]+f[v][1]-2*dis[u])/2);
		}
		if(f[v][0]>f[u][0]){
			if(g[v][0]!=g[u][0]&&f[u][0]>f[v][1]) f[u][1]=f[u][0],g[u][1]=g[u][0];
			else if(f[v][1]>f[u][1]||g[v][0]==g[u][1]) f[u][1]=f[v][1],g[u][1]=g[v][1];
			f[u][0]=f[v][0],g[u][0]=g[v][0];
		}
		else{
			if(f[v][0]>f[u][1]&&g[v][0]!=g[u][0]) f[u][1]=f[v][0],g[u][1]=g[v][0];
			else if(f[v][1]>f[u][1]&&g[v][1]!=g[u][0]) f[u][1]=f[v][1],g[u][1]=g[v][1];
		}
	}
}
}
int main(){
//	return system("fc data.out wronganswer1.ans"),0;
#ifdef LOCAL
	freopen("wronganswer1.in","r",stdin);
//	freopen("data.out","w",stdout);
#endif
	read(n);
	tree1::readedge();tree2::readedge();
	tree2::init();
	tree1::mian();
	for(int i=1;i<=n;++i) ans=max(ans,tree1::d[i]-tree2::dis[i]);
	println(ans);
	return 0;
}
posted @ 2022-05-11 19:04  cunzai_zsy0531  阅读(42)  评论(0编辑  收藏  举报