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; }

浙公网安备 33010602011771号