uestc 1717

 

http://www.acm.uestc.edu.cn/problem.php?pid=1717&cid=169

 

题意:先给一棵树,有n个结点(n<10^5),相邻的结点有距离;如果在这棵树上添加一条边后,查询两个点的距离是否改变近了,如果变近了,输出变近了多少。否则输出0。

 

这个题是一个很好的练dfs的题,比赛的时候是郭化权做的,自己去弄线段树去了,不过线段树也是做不出来的~~~~。赛后想了想,觉得不是很难,结果弄了两天才出来,悲剧呀。

还是郭化权的思路慎密。

 

思路:在树上面加入一条边后,一定会有一个环出现的。仔细想想之后,最后要处理的都会归到这个环上的,因为只有要环里面的才可能出现距离变近。基于这个想法,可以进行缩点,把不在这个环上的点都缩到这个环上来。

首先先求出这个环,简单的dfs就可以了,不过,题会出现两个点构成一个环的情况,这个比较难处理(当然,如果直接套tarjin模板上的话,应该就没什么问题了)。

然后从加入的那条边的一个点开始遍历整个环,到达另一个点,同时把距离都记录下来。

最后都查询的时候都直接找到环上的点来处理。

View Code
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<stdlib.h>
using namespace std;
const int N = 100005;
struct nd
{
    int to,w;
}tmp;
vector<nd>as[N];
int dis[N],Hash[N],ok,sta[N],vis[N];
//dis[N],记录距离
//Hash[N]记录当前点可以缩到环上的那个点
//dfs找环用的,
void dfs(int pre,int cur,int cnt)
{
    int i;
    sta[cnt] = cur;
    vis[cur] =  1;
    for(i = 0; i < (int)as[cur].size(); ++ i){
        tmp = as[cur][i];
        if(tmp.to != pre){
            if(vis[tmp.to]){//找到环了
                ok = 1;
                memset(vis,0,sizeof(vis));
                while(tmp.to!=sta[cnt]) vis[sta[cnt--]] = 1;
                vis[tmp.to] = 1;
                return ;
            }
            dfs(cur,tmp.to,cnt+1);
            if(ok)return;
        }
    }
    vis[cur] = 0;
}
//DFS从s点出发到达t点,并记录距离和进行缩点
void DFS(int pre,int cur,int idx,int fa,int cnt)
{
    int i;
    if(fa) Hash[cur] = fa;
    else{ Hash[cur] = cur; if(!dis[cur]) dis[cur] = cnt; }
    for(i = 0; i < (int)as[cur].size() ; ++ i){
        tmp = as[cur][i];
        if((tmp.to!=pre&&!Hash[tmp.to])||tmp.to==idx){
            if(vis[tmp.to]) DFS(cur,tmp.to,idx,0,cnt+tmp.w);
            else DFS(cur,tmp.to,idx,fa?fa:cur,cnt+tmp.w);
        }
    }
}
int main()
{
    int i,t,n,m,cas=0;
    int a,b,c,disa,disb,A,B;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        for(i = 0; i < N; ++ i)as[i].clear();
        for(i = 1; i <= n; ++ i)
        {
            scanf("%d %d %d",&a,&b,&c);
            tmp.to = b; tmp.w = c;
            as[a].push_back(tmp);
            tmp.to = a;
            as[b].push_back(tmp);
        }
        A = a; B = b;
        memset(dis,0,sizeof(dis));
        memset(Hash,0,sizeof(Hash));
        memset(vis,0,sizeof(vis));
        ok = 0; sta[0] = 1;
               vis[1] = 1; dfs(-1,1,1);
                if(!ok){ vis[1] = 0; vis[a] = 1; vis[b] = 1;}//如果ok为0,说明两点成环
                DFS(b,a,b,0,0);
                printf("Case #%d:\n",++cas);
        while(m--){
            scanf("%d %d",&a,&b);
            a = Hash[a];
            b = Hash[b];
            disa = abs(dis[a]-dis[b]);//没加边的时候的距离
            disb = abs(dis[A]-dis[b]) + c + abs(dis[B]-dis[a]);
            i = abs(dis[B]-dis[b]) + c + abs(dis[A]-dis[a]);
            if(i<disb) disb = i;//加后的距离
            if(disa<=disb)puts("0");
            else printf("%d\n",disa-disb);
        }
    }return 0;
}
                     

posted on 2012-06-12 19:15  aigoruan  阅读(372)  评论(0)    收藏  举报

导航