记录编号 261179 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [NOIP 2015]运输计划 最终得分 100
用户昵称 Gravatar再见 是否通过 通过
代码语言 C++ 运行时间 1.427 s
提交时间 2016-05-15 17:28:58 内存使用 16.63 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=300050;
int last[N];  //每个点的父节点 
int father[N];  //用于离线LCA and 用于树上找路
int dist[N];  //每个点到根的距离 
int deep[N];  //计算深度 
int line[N]; //最长路径的每一条边 
int dye[N];  //对最长路上的每个点染色 
int sum[N];  //前缀和 
int ans[N];  //离线LCA答案 
bool vis[N];  //离线LCA判断 
int all_size;  //最长路径上的边数 
int line_LCA;  //最长路径的LCA 
int Dist;  //最长路径的长度 
int n,m;  //n个点,m个路径 
struct Node {  //存路径 
    int x,y,lca,dist;
}node[N];
struct Edge {  //链表 
    int a,b;
    Edge* next;
};
Edge* g[N];  //存边和边权 
Edge* q[N];  // 存询问路径 
bool cmp(Node a,Node b){return a.dist>b.dist;}
inline int read() {
    char s=getchar();
    int x=0;
    while(s>'9'||s<'0')s=getchar();
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    return x;
}
void insert(Edge* &o,int a,int b) {     //链表插入 
    Edge* k=new Edge;
    k->a=a,k->b=b,k->next=o,o=k;
}
int find(int x) {     //非递归并查集 
    int j,k=x,r=x;
    while(r!=father[r]) r=father[r];
    while(k!=r) {
        j=father[k];
        father[k]=r;
        k=j;
    }
    return r;
}
void unionn(int a,int b) {      //并查集合并操作,father[a]=fb顺序不能变因为要在树上找路 
    int fa=find(a),fb=find(b);
    if(fa!=fb) father[fa]=fb;
}
void LCA(int x,int fa) {      //离线LCA同时计算每个点 的深度和到根节点的距离 
    int i;
    Edge* w;
    deep[x]=deep[fa]+1;
    ans[x]=x;
    w=g[x];
    while(w!=NULL) {
        if(w->a!=fa) {
            dist[w->a]=dist[x]+w->b;
            last[w->a]=x;
            LCA(w->a,x);
            unionn(w->a,x);
        }
        w=w->next;
    }
    vis[x]=true;
    w=q[x];
    while(w!=NULL) {
        if(vis[w->a]) {
            node[w->b].lca=ans[find(w->a)];
            node[w->b].dist=dist[x]+dist[w->a]-2*dist[node[w->b].lca];
        }
        w=w->next;
    }
}
void search(int x) {       //找每条路径与最长路径上公共线段 ,用并查集加速树上找路 
    int a=find(node[x].x),b=find(node[x].y),c=node[x].lca;
    while(a&&!dye[a]) {
        if(deep[a]<deep[c]) break;
        unionn(a,last[a]);
        a=find(a);
    }
    while(b&&!dye[b]) {
        if(deep[b]<deep[c]) break;
        unionn(b,last[b]);
        b=find(b);
    }
    if(deep[c]<deep[line_LCA]&&(dye[a]||dye[b])) !dye[a]?a=line_LCA:b=line_LCA;
    node[x].x=min(dye[a],dye[b]),node[x].y=max(dye[a],dye[b])-1;
}
bool ok(int x) {      //二分答案判断 
    int i,L=1,R=all_size;
    for(i=1;i<=all_size;i++) //对于最长路上的每条线段,如果它的长度>=Dist-x,说明该线段可选 
        if(line[i]>=Dist-x) sum[i]=sum[i-1]+1;
        else sum[i]=sum[i-1];
    if(sum[R]-sum[L-1]<=0) return 0;
    for(i=1;i<m&&node[i].dist>x;i++) {
        L=max(L,node[i].x);
        R=min(R,node[i].y);       //[L,R]和[l,r]求交 
        if(L>R) return 0; 
        if(sum[R]-sum[L-1]<=0) return 0;   //[L,R]上没有可选点 
    }
    return 1;
}
int main() {
	freopen("transport.in","r",stdin);
	freopen("transport.out","w",stdout);
    int i,x,y,z;
    n=read(),m=read();
    for(i=0;i<=n;i++) { 
        father[i]=i;      //初始化father,此时father用于离线LCA 
        g[i]=q[i]=NULL;
    }
    for(i=1;i<n;i++) {
        x=read(),y=read(),z=read();
        insert(g[x],y,z);
        insert(g[y],x,z);
    }
    for(i=0;i<m;i++) {
        node[i].x=read();
        node[i].y=read();
        insert(q[node[i].x],node[i].y,i);
        insert(q[node[i].y],node[i].x,i);
    }
    LCA(1,0);
    sort(node,node+m,cmp);
    line_LCA=node[0].lca;
    x=node[0].x,y=node[0].y,Dist=node[0].dist,i=0;
    all_size=deep[x]+deep[y]-2*deep[line_LCA];
    while(x!=line_LCA) {       //找最长线段上的的所有线段,并对每个最长路上的点染色 
        dye[x]=++i;
        line[i]=dist[x]-dist[last[x]];
        x=last[x];
    }
    dye[line_LCA]=++i,i=0;
    while(y!=line_LCA) {
        dye[y]=all_size+1-i;
        line[all_size-i++]=dist[y]-dist[last[y]];
        y=last[y];
    }
    for(i=0;i<=n;i++) father[i]=i; //初始化father,此时father用于树上找路 
    for(i=1;i<m;i++) search(i);
    int l=0,r=Dist,mid;
    while(l<r) {           //二分答案 
        mid=l+r>>1;
        if(ok(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d\n",r);
    return 0;
}