zoukankan      html  css  js  c++  java
  • NOIP2015运输计划的一个O(n)解法

    一个O(n)的解法。

    不难发现有如下性质:

    • 被改造成虫洞的边一定在最长路径上

    那么,我们用类似提直径的方法把这条路径给拎出来

    就会形成这样的一棵树。

    那么,对于一条边,若其被改成虫洞,最长路径会有如下三种情况:

    • 依然是当前的最长路径

    • 被改造边左端点的最长路径

    • 被改造边右端点的最长路径

    难道我们不用考虑经过该边的路径吗??

    当然不用。

    我们已经是在最长路径上改造边了,而最长路径是长于任何一条路径的(废话),那么经过该边的任何路径在此边被改造后依然短于最长路径,故不用考虑。

    至于求两端的最长路径,我们可以前缀和后缀分别维护一下。


    那么怎么求前缀(后缀)呢?

    我们可以在每个点开个vector,对于一条路径,我们把另一端点以及路径的编号加到vector中。

    然后,再开个vis数组,表示这个点是否被访问过。

    在遍历时,我们访问所有以这一点为一端点的路径标号(id)和另一端点(y),若另(vis[y]=true),就拿(id)的长度去更新即可。(口胡不清,这最好手动模拟一下)

    至于求路径长和LCA呢?

    额,树剖我是当常数看的。。。

    实在不行你可以Tarjan离线预处理啊

    代码(巨丑无比):

    #include<bits/stdc++.h>
    #define reg register int
    #define MAXN 300010
    using namespace std;
    int n,m,head[MAXN],dis[MAXN],tot,maxx,bh,pre[MAXN],sumfr[MAXN],sumla[MAXN],num[MAXN],lian[MAXN],ans=2e9+7;
    bool mark[MAXN],vis[MAXN];
    struct node {
    	int st,ed,lca,d;
    } P[MAXN];
    struct Edge {//前向星
    	int ed,v,last;
    } G[MAXN*2];
    struct que {
    	int ed,id;
    };
    vector<que> Q[MAXN];
    struct s__ {//树剖预处理
    	int son[MAXN],size[MAXN],top[MAXN],deep[MAXN];
    	void DFS1(int x,int fa,int v) {
    		dis[x]=dis[fa]+v;
    		pre[x]=fa;
    		deep[x]=deep[fa]+1;
    		size[x]=1;
    		for(int i=head[x]; ~i; i=G[i].last) {
    			int t=G[i].ed,v=G[i].v;
    			if(t==fa)continue;
    			DFS1(t,x,v);
    			size[x]+=size[t];
    			if(size[son[x]]<size[t])son[x]=t;
    		}
    	}
    	void DFS2(int x,int fa,int zu) {
    		top[x]=zu;
    		if(son[x])DFS2(son[x],x,zu);
    		for(int i=head[x]; ~i; i=G[i].last) {
    			int t=G[i].ed;
    			if(t==son[x]||t==fa)continue;
    			DFS2(t,x,t);
    		}
    	}
    	int LCA(int x,int y) {
    		while(top[x]!=top[y]) {
    			if(deep[top[x]]<deep[top[y]])swap(x,y);
    			x=pre[top[x]];
    		}
    		if(deep[x]>deep[y])swap(x,y);
    		return x;
    	}
    } shupou;
    void Rd(int &res) {//读优
    	res=0;
    	char ch=getchar();
    	while('0'>ch||ch>'9')ch=getchar();
    	while('0'<=ch&&ch<='9')res=(res<<3)+(res<<1)+(ch-'0'),ch=getchar();
    }
    void Add(int st,int ed,int v) {
    	tot++;
    	G[tot]=Edge {ed,v,head[st]};
    	head[st]=tot;
    }
    void DFS(int x,int fa,int &bb) {
    	num[x]=bb;
    	for(int i=head[x]; ~i; i=G[i].last) {
    		int t=G[i].ed;
    		if(t==fa)continue;
    		if(!mark[t])continue;
    		lian[bb]=G[i].v;
    		bb++;
    		DFS(t,x,bb);
    	}
    }
    void DFSla(int x,int fa,int zu) {//后缀
    	vis[x]=1;
    	for(int i=0; i<Q[x].size(); i++) {//访问vector
    		int t=Q[x][i].ed,id=Q[x][i].id;
    		if(vis[t]==1)sumla[zu]=max(sumla[zu],P[id].d);//更新
    	}
    	int nex=0;
    	for(int i=head[x]; ~i; i=G[i].last) {
    		int t=G[i].ed,v=G[i].v;
    		if(t==fa)continue;
    		if(mark[t]) {//优先遍历两旁伸出的子树
    			nex=t;
    			continue;
    		}
    		DFSla(t,x,zu);
    	}
    	if(nex) {
    		sumla[num[nex]]=sumla[num[x]];//更新下一个的后缀
    		if(nex==P[bh].st)return;//下一个点如果是另一端点的话就直接退出
    		DFSla(nex,x,num[nex]);
    	}
    }
    void DFSfr(int x,int fa,int zu) {//前缀
    	vis[x]=1;
    	for(int i=0; i<Q[x].size(); i++) {//访问vector
    		int t=Q[x][i].ed,id=Q[x][i].id;
    		if(vis[t]==1)sumfr[zu]=max(sumfr[zu],P[id].d);//更新
    	}
    	int nex=0;
    	for(int i=head[x]; ~i; i=G[i].last) {
    		int t=G[i].ed,v=G[i].v;
    		if(t==fa)continue;
    		if(mark[t]) {//优先遍历两旁伸出的子树
    			nex=t;
    			continue;
    		}
    		DFSfr(t,x,zu);
    	}
    	if(nex) {
    		sumfr[num[nex]]=sumfr[num[x]];//更新下一个的前缀
    		if(nex==P[bh].ed)return;//下一个点如果是另一端点的话就直接退出
    		DFSfr(nex,x,num[nex]);
    	}
    }
    int main() {
    	memset(head,-1,sizeof(head));
    	Rd(n),Rd(m);
    	for(int i=1; i<=n-1; i++) {
    		int x,y,z;
    		Rd(x),Rd(y),Rd(z);
    		Add(x,y,z);
    		Add(y,x,z);
    	}
    	shupou.DFS1(1,0,0);
    	shupou.DFS2(1,0,1);
    	for(int i=1; i<=m; i++) {
    		Rd(P[i].st),Rd(P[i].ed);
    		P[i].lca=shupou.LCA(P[i].st,P[i].ed);
    		P[i].d=dis[P[i].st]+dis[P[i].ed]-2*dis[P[i].lca];
    		if(P[i].d>maxx)maxx=P[i].d,bh=i;
    		Q[P[i].st].push_back(que {P[i].ed,i});
    		Q[P[i].ed].push_back(que {P[i].st,i});
    	}
    	int st=P[bh].st,ed=P[bh].ed,lca=P[bh].lca;
    	while(st!=lca)mark[st]=true,st=pre[st];
    	while(ed!=lca)mark[ed]=true,ed=pre[ed];
    	mark[lca]=true;
    	int bb=1;
    	DFS(P[bh].st,0,bb);
    	DFSfr(P[bh].st,0,num[P[bh].st]);
    	memset(vis,false,sizeof(vis));
    	DFSla(P[bh].ed,0,num[P[bh].ed]);
    	for(int i=1;i<=bb;i++){//更新最终答案
    		int res=0;
    		res=max(res,P[bh].d-lian[i]);
    		res=max(res,sumfr[i]);
    		res=max(res,sumla[i+1]);
    		ans=min(ans,res);
    	}
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    android:text 文字阴影设置
    android 布局的android:padding 和android:margin的区别
    sqlite的Query方法操作和参数详解
    SQL Server中如何让SQL语句对字符串大小写敏感
    android SQLite数据库(转)
    JAVA中内存分配的问题
    testview属性之详解
    在linux环境下安装VMtools(成功)
    关于配置文件
    C#的几种“属性”概念理解
  • 原文地址:https://www.cnblogs.com/SillyTieT/p/11359183.html
Copyright © 2011-2022 走看看