P2680 【运输计划】

绝对是一个全新的暴力思路。~只不过要开O2才能过。

接下来进入正题。

看到有一条边可以变成0,很自然地就想到要删边。
那么怎么判断一条边是不是在一条路径上呢?
设一条边的两个端点为u,v;
那条路径的两个端点为x,y;
我们把这个问题拆成两个小问题来做,
1先判定一个点是不是在那条路径上;
2再判断另一个点是不是也在那条路径上;
判定一个点是不是在一条路径上满足如下等式即可
设dis(i,j)为两点间的最短距离
dis(u,x)+dis(u,y)==dis(x,y)
至于证明用反证法
解决了这个之后接下来的就都是暴力了
细节会在代码里标注

#include<bits/stdc++.h>
using namespace std;
const int ll=1e6;
int head[ll],next[ll],uer[ll],ver[ll],wer[ll],tot;
int f[ll][30],d[ll],dis[ll],t;
void read(int &t)//偷来的快读
{
    char ch;
    while(ch = getchar())
        if(ch>='0' && ch<='9')
            break;

    t = ch - '0';
    while(ch = getchar())
        if(ch>='0' && ch<='9')
            t = (t<<3)+(t<<1)+ch-'0';
        else break;
}
void add(int u,int v,int w){//前向星存图
    tot++;
    uer[tot]=u;
    ver[tot]=v;
    wer[tot]=w;
    next[tot]=head[u];
    head[u]=tot;
}
int n,m;
void bfs(){
    queue<int> q;
    q.push(1);
    while(q.size()){
        int x=q.front();
        q.pop();
        for(int i=head[x]; i ; i=next[i]){
            int y=ver[i],w=wer[i];
            if(d[y]) continue;
            d[y]=d[x]+1;
            dis[y]=dis[x]+w;
            f[y][0]=x;
            for(int j=1;j<=t;j++)
                f[y][j]=f[f[y][j-1]][j-1];
            q.push(y);
        }
    }
}
int lca(int x,int y){
    if(d[x]<d[y]) swap(x,y);
    for(int i=t;i>=0;i--)
        if(d[f[x][i]]>=d[y])
            x=f[x][i];

    if(x==y) return x;

    for(int i=t;i>=0;i--)
        if(f[x][i]!=f[y][i])
        x=f[x][i],y=f[y][i];
    return f[x][0];
}
struct jh{
    int u,v,w;
}a[ll];//保存路径的起点,终点和长度
bool cmp(jh a,jh b){
    return a.w>b.w;
}
bool pd(int a,int b,int c,int len){
    return 2*dis[a]+dis[b]+dis[c]-2*(dis[lca(a,b)]+dis[lca(a,c)])==len; 
}
int main(){
    read(n); read(m);
    for(int i=1;i<=n-1;i++){
        int u,v,w;
        read(u); read(v); read(w);
        add(u,v,w);
        add(v,u,w);
    }
    t=log(n)/log(2)+1;
    d[1]=1;
    bfs();
    for(int i=1;i<=m;i++){
        read(a[i].u); read(a[i].v);
        a[i].w=dis[a[i].u]+dis[a[i].v]-2*dis[lca(a[i].u,a[i].v)];       
    }
    int ans=1<<30;
    sort(a+1,a+m+1,cmp);//把计划按照长度从大到小排序,
    for(int i=1;i<=2*(n-1);i+=2){
        int x=uer[i],y=ver[i],w=wer[i];//枚举删边
        int sum=0;
        for(int j=1;j<=m;j++){//由于路径长度是从大到小的,
            int flag=0;     //一旦sum不可能再被更新立即退出。
            int u=a[j].u,v=a[j].v;
            int fa=lca(u,v);
            int len=dis[u]+dis[v]-2*dis[fa];
            if(pd(x,u,v,len)&&pd(y,u,v,len)){
                if(sum<len-w)
                    sum=len-w;
                    flag=1;
                if(sum>ans) break;
            }
            else{
                if(sum<len){
                    sum=len;
                }

            }
            if(!flag) break;
        }
        ans=min(ans,sum);
    }
    printf("%d",ans);   
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值