NOIP2015_提高组Day2_3_运输计划

 

 

 

 

这题思路很简单;

先对每个询问求距离,对距离由大到小排序,

二分最小距离,验证是否可行,验证时用差分处理;

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iomanip>
#include<map>
#include<set>
#include<vector>
#include<ctime>
#include<cmath>
#define LL long long 
using namespace std;
#define LL long long 
#define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
#define max(x,y) ((x)<(y)?(y):(x))
#define min(x,y) ((x)<(y)?(x):(y))
#define FILE "1"
#define pii pair<int,int>
const int maxn=303000,inf=1000000000;
int read(){
    int x=0;bool flag=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')flag=1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return flag?-x:x;
}
int n,m;
struct node{
    int y,next,v;
}e[maxn<<1];
int linkk[maxn],len=0,fa[maxn],w[maxn],dep[maxn],top[maxn],siz[maxn],son[maxn],d[maxn];
void insert(int x,int y,int v){
    e[++len].y=y;
    e[len].v=v;
    e[len].next=linkk[x];
    linkk[x]=len;
}
int q[maxn],tail=0,head=0;
void dfs(int x){
    siz[x]=1;
    for(int i=linkk[x];i;i=e[i].next){
        if(e[i].y==fa[x])continue;
        fa[e[i].y]=x;w[e[i].y]=e[i].v;dep[e[i].y]=dep[x]+1;
        dfs(e[i].y);
        siz[x]+=siz[e[i].y];
        if(siz[e[i].y]>siz[son[x]])son[x]=e[i].y;
    }
}
void dfs2(int x){
    if(son[x]){
        top[son[x]]=top[x];
        d[son[x]]=d[x]+w[son[x]];
        dfs2(son[x]);
    }
    for(int i=linkk[x];i;i=e[i].next){
        if(e[i].y==fa[x]||e[i].y==son[x])continue;
        top[e[i].y]=e[i].y;
        d[e[i].y]=0;
        dfs2(e[i].y);
    }
}
pii lca(int x,int y){
    int u,v;
    int sum=0;
    while(x!=y){
        u=top[x],v=top[y];
        if(u==v)return make_pair((dep[x]>dep[y]?y:x),sum+abs(d[x]-d[y]));
        if(dep[u]>dep[v]){swap(x,y);swap(u,v);}
        sum+=d[y]+w[v];
        y=fa[v];
    }
    return make_pair(x,sum);
}
struct Node{
    int x,y,v,f;
    bool operator<(const Node &b)const{return v>b.v;}
}a[maxn];
int vis[maxn];
int maxx=0,flag=0;
void Dfs(int x){
    for(int i=linkk[x];i;i=e[i].next){
        if(e[i].y==fa[x])continue;
        Dfs(e[i].y);
        vis[x]+=vis[e[i].y];
    }
    if(vis[x]==flag)maxx=max(maxx,w[x]);
}
int work(int mid){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=mid;i++)vis[a[i].x]++,vis[a[i].y]++,vis[a[i].f]-=2;//比较暴力的差分
    //可以利用树链剖分将树划分成线段,在上面做差分,常数要比这个小的多,实测结果是#20会超
    maxx=0;flag=mid;Dfs(1);
    return maxx;
}
void slove(){
    n=read();m=read();
    int x,y,v;
    up(i,2,n){
        x=read(),y=read(),v=read();
        insert(x,y,v);insert(y,x,v);
    }
    dfs(1);top[1]=1;
    dfs2(1);
    pii temp;
    up(i,1,m){
        a[i].x=read(),a[i].y=read();
        temp=lca(a[i].x,a[i].y);
        a[i].v=temp.second;a[i].f=temp.first;
    }
    sort(a+1,a+m+1);
    int left=1,right=m,mid,ans=inf;//也可以1-a[1].v这样枚举,加个记忆化就好;
    while(left<=right){
        mid=(left+right)>>1;
        int fee=a[1].v-work(mid);
        if(fee>a[mid+1].v)right=mid-1;
        else left=mid+1;
        ans=min(ans,max(fee,a[mid+1].v));
    }
    cout<<ans<<endl;
}
int main(){
    freopen(FILE".in","r",stdin);
    freopen(FILE".out","w",stdout);
    slove();
    return 0;
}
//如果window下评测注意加个开栈
View Code

 

posted @ 2016-10-13 13:51  CHADLZX  阅读(250)  评论(0编辑  收藏  举报