bzoj 2282: [Sdoi2011]消防

来源:互联网 发布:网络文化节 编辑:程序博客网 时间:2024/06/10 01:52

题意:

在一棵树中选一条长度不超过s的路径,使其他所有城市到这条路径的最远距离最小。

题解:

YY下,可以发现这一段一定是在树的直径上。(具体怎么证我也说不清)
求出树的直径然后单调一下就好了。
code:

#include<cstdio>#include<cstdlib>#include<iostream>#include<cstring>using namespace std;int n,s;struct node{    int y,c,next;}a[600010];int len=0,last[300010];int st,ed,mx,son[300010],dis[300010],dep[300010];int q[300010],l=1,r=0;int ans=2147483647;int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void ins(int x,int y,int c){    a[++len].y=y;a[len].c=c;    a[len].next=last[x];last[x]=len;}void dfs(int x,int fa,int c){    dis[x]=dis[fa]+c;    for(int i=last[x];i;i=a[i].next)    {        int y=a[i].y;        if(y==fa) continue;        dfs(y,x,a[i].c);        if(dep[y]+a[i].c>dep[x]) dep[x]=dep[y]+a[i].c,son[x]=y;    }}void pre(int x,int fa,int op){    for(int i=last[x];i;i=a[i].next)    {        int y=a[i].y;        if(y==fa) continue;        if(op==1&&y==son[x]) continue;        pre(y,x,0);        dep[x]=max(dep[x],dep[y]+a[i].c);    }    if(son[x]&&op==1) pre(son[x],x,1);}int deepest;void solve(int x,int fa){    while(dis[x]-dis[fa]>s) fa=son[fa];    while(l<=r&&dis[q[l]]<dis[fa]) l++;    while(l<=r&&dep[q[r]]<=dep[x]) r--;    q[++r]=x;    int t=max(dep[q[l]],max(dis[fa],deepest-dis[x]));    ans=min(t,ans);    if(son[x]) solve(son[x],fa);}int main(){    n=read();s=read();    for(int i=1;i<n;i++)    {        int x,y,c;x=read();y=read();c=read();        ins(x,y,c);ins(y,x,c);    }    dis[0]=0;dfs(1,0,0);mx=-1;    for(int i=1;i<=n;i++) if(mx<dis[i]) mx=dis[i],st=i;    memset(son,0,sizeof(son));    memset(dep,0,sizeof(dep));    dfs(st,0,0);deepest=dep[st];    memset(dep,0,sizeof(dep));pre(st,0,1);    solve(st,st);    printf("%d",ans);}