【NOIP】提高组2015 运输计划

【题意】n个点的树,m条链,求将一条边的权值置为0使得最大链长最小。

【算法】二分+树上差分

【题解】

最大值最小化问题,先考虑二分最大链长。

对所有链长>mid的链整体+1(树上差分)。

然后扫一遍,对[在所有不满足链上]的边取最大值并check。

 

具体做法:对于二分的最大链长,将所有链长>mid的链取最大值(链的数量记为num),然后用树上差分整体+1。

树上差分:a+1,b+1,lca(a,b)-2。dfs的时候判断子节点的连边(若子节点权=num则连边参与比较),然后再把子节点权加进来。

最后看最大边权是否>=最大链长和最长链的差值。

复杂度O(n log n)。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
const int maxn=300010;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
int n,m,first[maxn],f[maxn][30],deep[maxn],dis[maxn],sum,num,mx,a[maxn],b[maxn],c[maxn],d[maxn],tot=0,s[maxn];
struct edge{int v,w,from;}e[maxn*2];
void insert(int u,int v,int w){tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
void dfs(int x,int fa){
    for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1];
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        deep[e[i].v]=deep[x]+1;
        dis[e[i].v]=dis[x]+e[i].w;
        f[e[i].v][0]=x;
        dfs(e[i].v,x);
    }
}
int lca(int x,int y){
    if(deep[x]<deep[y])swap(x,y);
    int d=deep[x]-deep[y];
    for(int i=0;(1<<i)<=d;i++)if((1<<i)&d)x=f[x][i];
    if(x==y)return x;
    for(int i=30;i>=0;i--)if((1<<i)<=deep[x]&&f[x][i]!=f[y][i]){
        x=f[x][i];y=f[y][i];
    }
    return f[x][0];
}
void DFS(int x,int fa){
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        DFS(e[i].v,x);//zhi xing shun xu
        if(s[e[i].v]>=num)mx=max(mx,e[i].w);
        s[x]+=s[e[i].v];
    }
}
bool check(int l){
    int cha=0;num=0;
    memset(s,0,sizeof(s));
    for(int i=1;i<=m;i++)if(d[i]>l){
        cha=max(cha,d[i]-l);
        s[c[i]]-=2;s[a[i]]++;s[b[i]]++;
        num++;
    }
    mx=0;sum=0;
    DFS(1,0);
    if(mx>=cha)return 1;else return 0;
}    
int main(){
    n=read();m=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        insert(u,v,w);insert(v,u,w);
    }
    dfs(1,0);
    int l=0,r=0,mid;
    for(int i=1;i<=m;i++){
        a[i]=read();b[i]=read();
        c[i]=lca(a[i],b[i]);
        d[i]=dis[a[i]]+dis[b[i]]-2*dis[c[i]];
        r=max(r,d[i]);
    }
    r++;
    while(l<r){
        mid=(l+r)>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    printf("%d",l);
    return 0;
}
View Code

dfs的时候注意操作顺序。

posted @ 2017-10-12 22:11  ONION_CYC  阅读(146)  评论(0编辑  收藏  举报