• 洛谷 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);
    }
    
  • 相关阅读:
    Win32应用中创建多窗口
    同时控制多个UIScrollView对象
    在后台代码中设定控件的Visibility
    VC中使用GDI+
    悬垂指针(Dangling pointer)和野指针(Wild pointer)
    在Windows下通过命令行界面编译并生成 .exe
    C++流重定向到文件
    读写文本文件和二进制文件——二进制模式
    使用MahApps.Metro
    WPF之GUI编写
  • 原文地址:https://www.cnblogs.com/ctz45562/p/11641538.html
Copyright © 2020-2023  润新知