洛谷 P4886 快递员

传送门

介是你没有体验过的(chuan)新做法。

暴力点分治算出每个点作为中心时的答案。

对于路径(c ightarrow u ightarrow c ightarrow v ightarrow c)的长度除以(2),我们可以看做(dis(u,v)+c)到路径((u,v))的距离乘(2)

把路径分成两类:

经过分治中心的:

对于这种路径,(dfs)一遍树,当脱离了某条路径时,比如走边((2,4))((2,5)),开始计算该路径的贡献。假设走边((x,y))脱离了该路径,则(y)子树内每个点(i)都会有(dis(i,x) imes 2+dis(u,v))的贡献。

具体实现:给每条边维护一个权值(V),对每条路径上的边的(V)(dis(u,v))(max)。算出每个点每条出边(V)的最大值和次大值。(dfs)时维护当前最大贡献(now),对每个点,等于该点最大值的边选次小值更新(now),否则选最大值,累加边权乘(2)即可。

这样会发现从根节点开始(dfs)时会出现这种情况:

此时,红色路径和绿色路径为最大和次大。

由于一条路径可能会占据根节点的两条出边,维护最大值和次大值就出(bug)了。

这样我们直接维护一个堆把每条路径加进去,从根节点出发时把每条经过该点的路径删掉,统计完再加回去。


不经过分治中心的:

((u,v))(lca)标记为关键点,并赋予一个权值(mx)所有标记它的点对中最大的(dis(u,v))。记(ma(i))(i)到其子树内所有关键点中(设关键点为(p)),最大的(mx(p)+dis(p,i) imes 2)

转移很简单:(ma(i)=max{mx(i),maxlimits_{edge(i,j)}{ma(j)+edge(i,j).l imes 2}})

对根维护一个(ma1)(ma2)表示根的儿子中最大和次大的(ma)

(dfs)根的每个儿子(i)的子树,同样的,若(ma(i)=ma1)(ma2),否则选(ma1)(now),对每个点(j)的贡献就是(now+dis(root,j) imes 2)

注意不用也不能在统计时进一步更新(now)了。


繁琐的细节:

  • 分类路径时,可以巧妙运用(tarjan lca)
  • 处理边的(V)时,其实就是链取(max)单点查。由于这些路径都经过根节点,直接对两端点取(max),统计子树最大值即可。
  • 直接位于路径((u,v))的点算不上((u,v))的贡献,需要额外处理,可以在统计(V)时一块算,免去了树剖两个(log)(lct)的大常数。
  • 由于一些神奇的原因,形如((u,u))的路径不会被算到,把(u)拆出来(u')连上长为(0)的边,把路径改为((u,u'))即可。

关于正确性:

如果一条路径经过根节点,就会统计上它对当前树内所有点的贡献。

否则会统计它对和它不属于根节点同一个儿子的子树的节点的贡献,然后分治它的子树。

我们就能把它对每个点的贡献算上。

关于复杂度:

随便想一想就会发现复杂度是(O((m+n)log n))的。

代码:

我的(priority\_queue)不知道为啥会在程序结束时炸掉于是手写的堆。

尽量精简了代码还是有(200+)行毕竟是暴力。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>

#define maxn 200005
#define inf 0x3f3f3f3f

using namespace std;

inline int read(){
	int x=0,y=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return y?-x:x;
}
namespace origin{
	int h[maxn],num;
	struct edge{
		int pre,to,l;
	}e[maxn<<1];
	inline void add(int from,int to,int l){
		e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l;
		e[++num].pre=h[to],h[to]=num,e[num].to=from,e[num].l=l;
	}
}
int h[maxn],siz[maxn],top[maxn],son[maxn],deep[maxn],fa[maxn],F[maxn],qu[maxn][2],num,all,mx,root,cnt;
int ma1[maxn],ma2[maxn],ema1[maxn],ema2[maxn],ans[maxn],dis[maxn],ee[maxn],bl[maxn],sp[maxn];
vector<pair<int,int> >v[maxn];
vector<int>poi[maxn];
bool vis[maxn],ap[maxn];
struct heap{
	int H[maxn],heap_size;
	void push(int d){
		int now=++heap_size;
		H[heap_size]=d;
		while(now>1){
			if(H[now]<H[now>>1]) return;
			swap(H[now],H[now>>1]);
			now>>=1;
		}
	}
	void pop(){
		int now=1,next;
		H[1]=H[heap_size--];
		while((now<<1)<=heap_size){
			next=now<<1;
			if(next<heap_size&&H[next+1]>H[next]) next++;
			if(H[now]>H[next]) return;
			swap(H[now],H[next]);
			now=next;                      
		} 
	}
	bool empty(){return !(bool(heap_size));}
};
struct del_heap{
	heap q,d;
	void push(int x){q.push(x);}
	void del(int x){d.push(x);}
	void clear(){q.heap_size=d.heap_size=0;}
	int top(){
		while(!d.empty()&&q.H[1]==d.H[1])q.pop(),d.pop();
		if(q.empty())return -inf;
		return q.H[1];
	}
}q;
struct edge{
	int pre,to,l,v;
}e[maxn<<1];
int find(int x){
	if(F[x]==x)return x;
	return F[x]=find(F[x]);
}
inline void add(int from,int to,int l){
	e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l,e[num].v=-inf;
}
void dfs1(int node=1){
	siz[node]=1;
	int x;
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(siz[x])continue;
		fa[x]=node,deep[x]=deep[node]+1,dis[x]=dis[node]+e[i].l;
		dfs1(x),siz[node]+=siz[x];
		if(siz[x]>siz[son[node]])son[node]=x;
	}
}
void dfs2(int node=1){
	vis[node]=1;
	if(son[node]){
		top[son[node]]=top[node],dfs2(son[node]);
		int x;
		for(register int i=h[node];i;i=e[i].pre){
			x=e[i].to;
			if(!vis[x])top[x]=x,dfs2(x);
		}
	}
}
inline int lca(int x,int y){
	while(top[x]!=top[y])deep[top[x]]<deep[top[y]]?y=fa[top[y]]:x=fa[top[x]];
	return deep[x]<deep[y]?x:y;
}
inline void check(int &m1,int &m2,int x){
	if(x>m1)m2=m1,m1=x;
	else if(x>m2)m2=x;
}
void getroot(int node,int f){
	int x,ma=0;
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(x==f||vis[x])continue;
		getroot(x,node),ma=max(ma,siz[x]);
	}
	ma=max(ma,all-siz[node]);
	if(ma<mx)mx=ma,root=node;
}
void init(int node,int f,int b){
	int x;
	bl[node]=b,ema1[node]=ema2[node]=ma1[node]=ma2[node]=-inf,ap[node]=1,F[node]=node;
	for(vector<pair<int,int> >::iterator iter=v[node].begin();iter!=v[node].end();++iter)
		if(ap[iter->first]){
			x=find(iter->first);
			if(x!=root)ma1[x]=max(ma1[x],iter->second);
			else {
				ee[iter->first]=max(ee[iter->first],iter->second),ee[node]=max(ee[node],iter->second);
				ma2[node]=max(ma2[node],iter->second),poi[b].push_back(iter->second);
				if(iter->first!=root)poi[bl[iter->first]].push_back(iter->second),ma2[iter->first]=max(ma2[iter->first],iter->second);
				q.push(iter->second);
			}
		}
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(x==f||vis[x])continue;
		init(x,node,b),F[x]=node,ma1[node]=max(ma1[node],ma1[x]+(e[i].l<<1));
	}
}
void calc(int node,int f,int now){
	siz[node]=1,ans[node]=max(ans[node],now);
	int x;
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(x==f||vis[x])continue;
		calc(x,node,max(e[i].v==ema1[node]?ema2[node]:ema1[node],max(now,ma2[node]))+(e[i].l<<1));
		siz[node]+=siz[x];
	}
}
void calc_edge_value(int node,int f){
	int x;
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(x==f||vis[x])continue;
		calc_edge_value(x,node);
		ee[node]=max(ee[node],ee[x]),check(ema1[node],ema2[node],e[i].v=ee[x]);
	}
	ans[node]=max(ans[node],ee[node]);
}
void clear(int node,int f){
	ap[node]=0,ee[node]=-inf;
	int x;
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(x==f||vis[x])continue;
		clear(x,node),e[i].v=-inf;
	}
}
void solve(int node){
	q.clear(),vis[node]=1;
	int x;
	ma1[node]=ma2[node]=-inf,F[node]=node,ap[node]=1,bl[node]=0;
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(vis[x])continue;
		init(x,node,x),F[x]=node,check(ma1[node],ma2[node],ma1[x]+(e[i].l<<1));
	}
	calc_edge_value(node,0);
	ans[node]=max(ans[node],ma1[node]);
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(vis[x])continue;
		for(register int j=0;j<poi[x].size();++j)q.del(poi[x][j]);
		calc(x,node,max((ma1[x]+(e[i].l<<1)==ma1[node]?ma2[node]:ma1[node]),q.top())+(e[i].l<<1));
		for(register int j=0;j<poi[x].size();++j)q.push(poi[x][j]);
		poi[x].clear();
	}
	clear(node,0);
	for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(vis[x])continue;
		mx=inf,all=siz[x],getroot(x,node),solve(root);
	}
}
void rebuild(int node=1,int f=0,int gf=0){
	int x;	
	if(node>all)for(register int i=h[node];i;i=e[i].pre){
		x=e[i].to;
		if(x==f)continue;
		rebuild(x,node,f);
	}
	else for(register int i=origin::h[node];i;i=origin::e[i].pre){
		x=origin::e[i].to;
		if(x==f||x==gf)continue;
		if(sp[x])add(node,sp[x],origin::e[i].l),add(sp[x],node,origin::e[i].l),add(sp[x],x,0),add(x,sp[x],0),x=sp[x];
		else add(node,x,origin::e[i].l),add(x,node,origin::e[i].l);
		rebuild(x,node,f);
	}
}
int DIS(int x,int y){
	return dis[x]+dis[y]-(dis[lca(x,y)]<<1);
}
int main(){
	memset(ee,~0x3f,sizeof ee);
	int n=all=read(),m=read(),k=n,x,y,z,res=inf;
	for(register int i=1;i<n;++i)x=read(),y=read(),z=read(),origin::add(x,y,z);
	for(register int i=1;i<=m;++i){
		qu[i][0]=x=read(),qu[i][1]=y=read();
		if(x==y&&!sp[x])sp[x]=++k,qu[i][1]=k;
	}
	if(sp[1])add(sp[1],1,0),add(1,sp[1],0);
	rebuild(1,sp[1]),dfs1(),dfs2();	
	for(register int i=1;i<=m;++i){
		x=qu[i][0],y=qu[i][1],z=dis[x]+dis[y]-(dis[lca(x,y)]<<1);
		v[x].push_back((pair<int,int>){y,z}),v[y].push_back((pair<int,int>){x,z});
	}
	memset(vis,0,sizeof vis);
	all=k,mx=inf,getroot(1,0),solve(root);
	for(register int i=1;i<=n;++i)res=min(res,ans[i]);
	printf("%d
",res);
}
原文地址:https://www.cnblogs.com/ctz45562/p/11641538.html