记录编号 269331 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [NOIP 2015]运输计划 最终得分 100
用户昵称 GravatarSky_miner 是否通过 通过
代码语言 C++ 运行时间 1.232 s
提交时间 2016-06-13 14:31:51 内存使用 38.46 MiB
显示代码纯文本
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
inline void read(int &x){
	x=0;char ch;
	while(ch=getchar(),ch<'!');
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');
}
inline int cat_max(const int &a,const int &b){return a>b ? a:b;}
inline int cat_min(const int &a,const int &b){return a<b ? a:b;}
const int maxn = 500010;
struct Edge{
	int to,next,dis;
}G[maxn*2];//
int head[maxn],ecnt;
void add1(int u,int v,int d){
	G[++ecnt].to = v;
	G[ecnt].dis = d;
	G[ecnt].next = head[u];
	head[u] = ecnt;
}
struct Save{
    int s,t;
}cmd[maxn];
int deep[maxn],fa[maxn],siz[maxn],top[maxn],vis[maxn];
//  深度       父亲     节点数  重链顶端节点 访问标记
int dis[maxn],t_w[maxn],son[maxn];//son就不解释了
// 到根的距离 边权压到节点上
void dfs(int u){
	vis[u]=1;
	siz[u]=1;
	for(int i=head[u];i;i=G[i].next){
		int to=G[i].to;
		if(vis[to]) continue;
		fa[to]=u;// 记录父亲 
		deep[to]=deep[u]+1;//深度 
		dis[to]=dis[u]+G[i].dis;//到根节点的距离 
		t_w[to]=G[i].dis;//把边权加到点上 
		dfs(to);
		siz[u]+=siz[to];//子树大小 
		if(siz[son[u]]<siz[to])//找重儿子 
			son[u]=to;
	}
} 
int map[maxn],cnt;
void init(const int &x,const int &y){//计算top,map数组
	top[x] = y;//top一条重链上的顶端
    map[x] = ++cnt;//节点在dfs序上的对应
    if( son[x] != 0 ) init(son[x],y);//应先搜重(zhong)儿子
    for(int i=head[x];i;i=G[i].next){
        if( G[i].to != son[x] && G[i].to != fa[x] ){//重儿子已搜过,不可以搜索父亲
            init(G[i].to,G[i].to);//(出了这一条重链)
        }
    }
}
int lca(int a,int b){
    while(top[a] != top[b]){//不处于同一条重链
        deep[top[a]] < deep[top[b]] ? swap(a,b),1:0;//选择深度大的向上提
        a = fa[ top[a] ];//我提 !!!!
    }
    //此时一定在同一条重链上
    return deep[a] < deep[b] ? a:b;//选择深度小的
}
int T[maxn],n,m;
void add(int s,int t){if(s > t) return;++T[s];--T[t+1];}//O(1)神奇大法
void doo(int x,int y){
    while(top[x] != top[y]){
        deep[top[x]] < deep[top[y]] ? swap(x,y),1:0;
        add(map[ top[x] ],map[x]);//求lca时对边附加信息
        x = fa[ top[x] ];
    }
    deep[x] > deep[y]?  swap(x,y),1:0;
    add(map[ son[x] ] ,map[y]);//附加信息
    return;
}
int ans[maxn],max_f=0;
bool check(int x){
    memset(T,0,sizeof(T));cnt = 0;
    for(int i=1;i<=m;++i){
        if(ans[i] > x ) ++cnt,doo(cmd[i].s,cmd[i].t);//需要虫洞
    }
    if( !cnt ) return true;
    for(int i=2;i<=n;++i) T[i] += T[i-1];//求前缀和O(1)神奇大法 
    for(int i=2;i<=n;++i)
        if(T[map[i]]== cnt && max_f-t_w[i] <= x) return true;
        //在这几条超限边的重合部分,如果改造为虫洞成为可行解,则return true,否则return false;
    return false;
}
int main(){
    freopen("transport.in","r",stdin);
    freopen("transport.out","w",stdout);
	read(n),read(m);
	int u,v,d;
	for(int i=1;i<n;++i){
		read(u),read(v),read(d);
		add1(u,v,d),add1(v,u,d);
	}
	deep[1] = 1;
	dfs(1);
	init(1,1);
    for(int i=1;i<=m;++i){//储存边
        read(cmd[i].s),read(cmd[i].t);
        ans[i] = dis[cmd[i].s] + dis[cmd[i].t] - 2*dis[lca(cmd[i].s,cmd[i].t)];
        max_f = cat_max(max_f,ans[i]);
    }
    int l=0,r=max_f,mid,ret;
    while(l<=r){//二分答案
        mid = (l+r) >> 1;
        if( check(mid) ){
            ret = mid,r = mid-1;
        }else l = mid + 1;
    }
    printf("%d",ret);
    fclose(stdin),fclose(stdout);return 0;
}