题意:给你一颗顶点数为n的树,然后再给出一条边,连接这棵树的两个顶点。然后有q条询问,每条询问给出两个点a、b,问从a-b走最后给出的边是否能节省路程,若能则输出节省的路程,若不能输出0。
思路:这道题我感觉自己想的比较麻烦,不知道有没有其他的算法。首先,由于这个图本来是棵树,加了一条边就形成了一个环,从a-b所走的路有两种情况:
①经过所给出的那条边
②不经过所给出的那条边
对于这个环上的点v,它的子树上的点要到这棵树以外的其他节点必须通过v,因此这个点子数上的所有点都相当于v。于是把所有的树叶一个一个删掉,最后会形成一个环。
问题就变成了在这个环上的的a点到b点经过给出的边所走的路程是否大于不经过所给出的那条边所走的路程。这两个路线用dfs搜一下就好,这里要注意经过所给出的那条边所走的路程有两种走法,只求出一种,然后环的长度减去这个长度就得到另外一种走法的路程了,然后取这两个数的较小的值就行了。
这题开始时没看到最后那个边是加上去的,题意也理解的不太对,写的十分蛋疼,还不对,改了好多遍,因此代码写的比较丑……
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-8
#define pi acos(-1.0)
using namespace std;
const int maxn=100000+10;
int du[maxn],tidai[maxn],way1[maxn],way2[maxn];
bool vis[maxn],treeleaf[maxn];
int n;
struct Dir
{
int t,len;
};
vector<Dir>g[maxn];
int len1,len2;
void dfs(int u,int f,int c) //把环上点的子树都都用这个点替代
{
int s=g[u].size();
int v;
for(int i=0;i<s;++i)
{
v=g[u][i].t;
if(treeleaf[v]&&v!=f)
{
tidai[v]=c;
dfs(v,u,c);
}
}
}
void dfs2(int u,int f,int s,int e,int step)
{
int sn=g[u].size();
int v;
for(int i=0;i<sn;++i)
{
v=g[u][i].t;
if(v!=f&&!treeleaf[v])
{
if(f==-1&&v==e) continue;
if(v==s)
{
break;
}
step+=g[u][i].len;
way1[v]=step;
dfs2(v,u,s,e,step);
step-=g[u][i].len;
}
}
}
void dfs3(int u,int f,int s,int e,int step)
{
int sn=g[u].size();
int v;
for(int i=0;i<sn;++i)
{
v=g[u][i].t;
if(v!=f&&!treeleaf[v])
{
if(f==-1&&v!=e) continue;
if(v==s)
{
way2[n+1]=step+g[u][i].len;
break;
}
step+=g[u][i].len;
way2[v]=step;
dfs3(v,u,s,e,step);
step-=g[u][i].len;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t,qt,tcase=0;
scanf("%d",&t);
while(t--)
{
tcase++;
scanf("%d%d",&n,&qt);
int a,b,c;
Dir temp;
for(int i=0;i<=n;++i)
g[i].clear();
memset(du,0,sizeof(du));
memset(treeleaf,0,sizeof(treeleaf));
int x,y;
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&a,&b,&c);
temp.t=b;temp.len=c;
g[a].push_back(temp);
temp.t=a;temp.len=c;
g[b].push_back(temp);
du[a]++;
du[b]++;
if(i==n)
{
x=a;
y=b;
}
}
queue<int>q;
for(int i=1;i<=n;++i) //找出所有的树叶
{
if(du[i]==1)
{
q.push(i);
treeleaf[i]=true;
}
}
int u,s,v;
while(!q.empty()) //删除叶子所在的边,直到只剩下一个环
{
u=q.front();
q.pop();
s=g[u].size();
for(int i=0;i<s;++i)
{
v=g[u][i].t;
du[v]--;
if(du[v]==1)
{
treeleaf[v]=true;
q.push(v);
}
}
}
for(int i=1;i<=n;++i)
{
if(!treeleaf[i])
{
tidai[i]=i;
dfs(i,-1,i);
}
}
way1[x]=0;
way2[x]=0;
len1=len2=-1;
dfs2(x,-1,x,y,0); //不通过所给边所需要走的路程
dfs3(x,-1,x,y,0); //通过所给边所需要走的路程
int ans1=0,ans2=0;
printf("Case #%d:\n",tcase);
for(int i=0;i<qt;++i)
{
scanf("%d%d",&a,&b);
a=tidai[a];
b=tidai[b];
ans1=0;ans2=0;
ans1=abs(way1[a]-way1[b]);
ans2=abs(way2[a]-way2[b]);
ans2=min(ans2,way2[n+1]-ans2);
if(ans1>ans2) ans1=ans1-ans2;
else ans1=0;
printf("%d\n",ans1);
}
}
return 0;
}