BZOJ(本校) 3044 旅行 - 树形dp&基环树

时限:1s 内存:64MB
题目描述
Bob发现字节大地上有个城市形成了一个树形结构。作为一个旅游爱好者,Bob想要在个城市之间穿梭。经过调查,Bob知道了每条路的花费。由于时间对他来说很宝贵,他希望字节大地的国王能够修建一条新路来节省时间。现在Bob有个旅行计划,每个旅行计划的起点和终点也都知道,Bob希望你能帮助他计算,通过新建一条道路,他能节省多少时间。Bob总是会选择最短路线进行旅行。如果新建一条道路不能节省时间,那么答案为。

输入格式
输入文件的第一行包含两个整数和,分别表示城市个数和旅行计划的个数。接下来行,每行包含三个整数和,表示Bob需要花费单位时间通过城市和之间的道路。前条路表示原来的树形结构,最后一条路表示新修的道路。接下来行询问,每行包含两个整数和,表示Bob计划从旅行到。

输出格式
对于每个询问,输出一行表示表示该询问的答案。

样例输入
5 5
1 2 3
2 3 4
4 1 5
3 5 1
3 1 5
1 2
1 3
2 5
3 4
4 5

样例输出
0
2
0
2
2

分析:

  • 注意都是正权边。
  • 本来是一棵树的,可以用树形dp搞出每个点到根的路径费用,求出lca,转而求出两点之间的费用。
  • 后来加了一条边,形成了一棵基环树。
    • 找到那个环,对环上的每个点所领导的子树染色,并处理出此时每个点到它所属的根(这些根就是环上的点)的费用。
    • 对于询问的两个点,如果是同一种颜色,就是0(新添加的边对它没有作用);如果不是同一种颜色,u到其根的费用+v到其根的费用+u的根到v的根的费用(这个可以用min(原始树上两点的费用,环上的费用和-原始树上两点的费用)求出)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100000
#define MAXLOG 20

struct node{
    int v,w;
    node *next;
}edge[MAXN*2+10],*adj[MAXN+10],*ecnt=&edge[0];

int n,Q,P[MAXN+10][MAXLOG+10],dep[MAXN+10],fa[MAXN+10],Logn,sum;
int b[MAXN+10],c[MAXN+10];
int col[MAXN+10],pnt[MAXN+10],cntp;
bool vis[MAXN+10],tag[MAXN+10],cir[MAXN+10];

void addedge(int u,int v,int w)
{
    node *p=++ecnt;
    p->v=v,p->w=w;
    p->next=adj[u];
    adj[u]=p;
}
void dfs1(int u)
{
    vis[u]=true;
    for(node *p=adj[u];p;p=p->next){
        if(vis[p->v])
            continue;
        fa[p->v]=u,dep[p->v]=dep[u]+1;
        b[p->v]=b[u]+p->w;
        dfs1(p->v);
    }
}
void LCA_pre()
{
    memset(P,-1,sizeof P);
    for(int i=1;i<=n;i++)
        P[i][0]=fa[i];
    for(Logn=0;(1<<Logn)<=n;Logn++);
    Logn--;
    for(int j=1;j<=Logn;j++)
        for(int i=1;i<=n;i++)
            if(P[i][j-1]!=-1)
                P[i][j]=P[P[i][j-1]][j-1];
}
int LCA(int x,int y)
{
    if(dep[x]>dep[y])
        swap(x,y);
    int Logy;
    for(Logy=0;(1<<Logy)<=dep[y];Logy++);
    Logy--;
    for(int i=Logy;i>=0;i--)
        if(dep[P[y][i]]>=dep[x])
            y=P[y][i];
    if(x==y) return x;
    for(int i=Logy;i>=0;i--)
        if(P[x][i]!=-1&&P[y][i]!=-1&&P[x][i]!=P[y][i])
            x=P[x][i],y=P[y][i];
    return P[x][0];
}
void dfs2(int u,int mark)
{
    vis[u]=true,col[u]=mark;
    for(node *p=adj[u];p;p=p->next){
        if(cir[p->v]||vis[p->v]) continue;
        c[p->v]=c[u]+p->w;
        dfs2(p->v,mark);
    }
}
void Circle()
{
    int x,y,w;
    scanf("%d%d%d",&x,&y,&w);
    int lca=LCA(x,y);
    sum=b[x]+b[y]-2*b[lca]+w;
    for(int u=x;u!=lca;u=fa[u])
        cir[u]=true,pnt[++cntp]=u;
    for(int u=y;u!=lca;u=fa[u])
        cir[u]=true,pnt[++cntp]=u;
    cir[lca]=true,pnt[++cntp]=lca;
    addedge(x,y,w);
    addedge(y,x,w);
    memset(vis,0,sizeof vis);
    for(int i=1;i<=cntp;i++){
        c[pnt[i]]=0;
        dfs2(pnt[i],pnt[i]);
    }
}
void read()
{
    int x,y,w;
    scanf("%d%d",&n,&Q);
    for(int i=1;i<n;i++){
        scanf("%d%d%d",&x,&y,&w);
        addedge(x,y,w);
        addedge(y,x,w);
    }
    fa[1]=0,dep[1]=1,b[1]=0;
    memset(vis,0,sizeof vis);
    dfs1(1);
    LCA_pre();
    Circle();
}
int dist(int x,int y)
{
    int lca=LCA(x,y);
    return min(b[x]+b[y]-2*b[lca],sum-b[x]-b[y]+2*b[lca]);
}
int main()
{
    int x,y;
    read();
    while(Q--){
        scanf("%d%d",&x,&y);
        int d=c[x]+c[y]+dist(col[x],col[y]);
        int lca=LCA(x,y);
        if(col[x]==col[y]){
            printf("0\n");
            continue;
        }
        if(d<b[x]+b[y]-2*b[lca])
            printf("%d\n",b[x]+b[y]-2*b[lca]-d);
        else
            printf("0\n");
    }
}

转载于:https://www.cnblogs.com/katarinayuan/p/6572805.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值