Description
为了 随时 与 rainbow快速交流, Freda制造了 两部传呼机 。Freda和 rainbow所在的地方有N座房屋、M条双向 光缆 。每条光缆连接两座房屋, 传呼机发出的信号只能沿着光缆传递,并且 传呼机的信号 从光缆的其中一端传递到另需要花费 t单位时间 。现在 Freda要 进行 Q次试验, 每次选取两座房屋,并想知道 传呼机的信号在这两座房屋之间传递 至少需 要多长时间。 Freda 和 rainbow简直弱爆了有木有T_TT_T ,请你帮他们吧……
N座房屋 通过光缆 一定是连通的, 并且这 M条光缆有以下三类连接情况:
A:光缆不形成环 ,也就是光缆仅 有 N-1条。
B:光缆只 形成一个环,也就是光缆 仅有 N条。
C:每条光缆仅在一个环中。
Solution
声明一下ABC类数据的多少
送分数据占10%,2<=N<=1000,N-1<=M<=1200。
A类数据占30%,M=N-1。
B类数据占50%,M=N。
C类数据占10%,M>N。
对于100%的数据,2<=N<=10000,N-1<=M<=12000,Q=10000,1<=x,y<=N,1<=t<=32768。
送分数据——直接 sp(b)fa ,稍微记忆一下
- A类数据——树
- B类数据——环套树
- C类数据——仙人掌
仙人掌就是许多个环由链串在一起,每条边至多在一个环中。
先考虑A类
显然直接
O(N)
求出根到每个节点的距离再用
lca
之类的东西搞搞就好
B类环套树
环太恶心,我们考虑删边
你可以先用一个DFS把环搞出来,随便找一条必定在环上的边打上标记不能走
然后像树一样搞一遍,算最后答案的时候要取在树上的答案和强制走这条边的最小值。
关键在C类(话说只有10分啊!!栋爷:然而10分让你无法AC~~)
首先我们从根开始跑一遍
sp(b)fa
,求出每个点到根的最短距离,设之为
dis[]
。
现在我们需要把仙人掌弄成一个类似树的东西。
我们将从根
DFS
到的环上第一个点称为环顶(注意,环顶并不属于这个环,因为其有可能还会属于别的环)
DFS
的时候,如果我们碰到了一个已经走过的点,显然出现了环。
设
dfn[i]
表示第
i
个点在搜索树上的深度
设已走过的那个点是
如果
dfn[j]>dfn[i]
,那么说明这个环已经处理过了,不理他。
如果
dfn[j]<dfn[i]
,显然
j
就是这个环的环顶。
我们需要记录一个
知道这个环的环顶以后,我们可以把每个点都与环顶连一条边,然后把环上其他边都删掉(也可以在刚刚的过程中顺便做)
这样,我们得到了一棵树,暂且称为仙人掌树
然后再来一个
现在考虑求解。
对于两个点
x,y
,设
lp=lca(x,y),u
表示从
x
向上走到
若
若
u,v
在同一个环上,那么显然
lp
也在这个环上。我们要取
u
到
这时候刚才求得
D
和
先上图
假设
D
的方向是原图中的
那么
取个
min
再加上
dis[x]−dis[u]
和
dis[y]−dis[v]
就是答案
Code
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note
{
int x,y,z,f,b;
};
bool cmp(note x,note y)
{
return x.x<y.x;
}
int n,m,q,a1[200005][2],delt,num;
note a[100000];
int dis[100005],d[100005],ft[100005][15],deep[200005],hf[200005],fx[100005],lg[200005],from[100005];
bool bz[100005],b1;
void spfa(int l)
{
int i,j,now;
memset(bz,0,sizeof(bz));
i=0;
j=1;
d[1]=l;
bz[l]=1;
dis[l]=0;
while (i<j)
{
i++;
int k;
now=d[i];
if (a1[now][0]>0)
fo(k,a1[now][0],a1[now][1])
{
int p=a[k].y;
if (dis[p]>dis[now]+a[k].z)
{
dis[p]=dis[now]+a[k].z;
if (bz[p]==0)
{
d[++j]=p;
bz[p]=1;
}
}
}
bz[now]=0;
}
}
void back(int k,int f)
{
if (k!=delt)
{
d[k]=d[a[f].y]+a[f].z;
a[f].b=1;
if (a[from[k]].x!=delt)
{
m++;
a[2*m-1].x=a[2*m].y=delt;
a[2*m-1].y=a[2*m].x=k;
a[2*m-1].f=a[2*m].f=m;
a[2*m-1].b=a[2*m].b=0;
}
hf[k]=num;
lg[num]+=a[f].z;
back(a[from[k]].x,from[k]);
}
else
{
lg[num]+=a[f].z;
delt=0;
}
}
void findh(int k,int f,int dep)
{
int i;
deep[k]=dep;
from[k]=f;
fo(i,a1[k][0],a1[k][1])
{
int p=a[i].y;
if (a[i].f!=a[f].f)
{
if(deep[p]==0)
{
findh(p,i,dep+1);
}
else if (deep[k]>deep[p])
{
delt=p;
hf[k]=++num;
d[k]=a[i].z;
lg[num]+=a[i].z;
back(a[f].x,f);
}
}
}
}
void dfs(int k,int f,int dep)
{
ft[k][0]=f;
deep[k]=dep;
int i;
fo(i,a1[k][0],a1[k][1])
{
int p=a[i].y;
if (p!=f&&a[i].b==0&&a[a[i].f].b==0)
{
dfs(p,k,dep+1);
}
}
}
int lca(int x,int y,int &u,int &v)
{
if (deep[x]<deep[y]) swap(x,y);
while(deep[x]!=deep[y])
{
int i=0;
while(deep[ft[x][i]]>deep[y]) i++;
if (i==0)
{
if (ft[x][0]==y)
{
u=v=x;
return ft[x][0];
}
x=ft[x][0];
break;
}
x=ft[x][--i];
}
while (x!=y)
{
int i=0;
while (ft[x][i]!=ft[y][i]) i++;
if (i==0)
{
u=x;
v=y;
return ft[x][0];
}
x=ft[x][--i];
y=ft[y][i];
}
}
int main()
{
freopen("call.in","r",stdin);
freopen("call.out","w",stdout);
cin>>n>>m>>q;
int i,j;
fo(i,1,m)
{
scanf("%d%d%d",&a[2*i-1].x,&a[2*i-1].y,&a[2*i-1].z);
a[2*i].x=a[2*i-1].y;
a[2*i].y=a[2*i-1].x;
a[2*i].z=a[2*i-1].z;
a[2*i].f=a[2*i-1].f=i;
a[2*i].b=a[2*i-1].b=0;
}
sort(a+1,a+2*m+1,cmp);
fo(i,1,2*m)
{
if (a[i].x!=a[i-1].x)
{
a1[a[i].x][0]=i;
a1[a[i-1].x][1]=i-1;
}
}
a1[a[2*m].x][1]=2*m;
memset(dis,107,sizeof(dis));
bz[1]=1;
spfa(1);
num=0;
delt=0;
findh(1,0,1);
sort(a+1,a+2*m+1,cmp);
fo(i,1,2*m)
{
if (a[i].x!=a[i-1].x)
{
a1[a[i].x][0]=i;
a1[a[i-1].x][1]=i-1;
}
if (fx[a[i].f]==0) fx[a[i].f]=i;
else
{
a[fx[a[i].f]].f=i;
a[i].f=fx[a[i].f];
}
}
a1[a[2*m].x][1]=2*m;
memset(deep,0,sizeof(deep));
dfs(1,0,1);
fo(i,1,trunc(log(n)/log(2)))
fo(j,1,n) ft[j][i]=ft[ft[j][i-1]][i-1];
fo(i,1,q)
{
int x,y,u,v;
scanf("%d%d",&x,&y);
if (x==y)
{
printf("0\n");
continue;
}
int lp=lca(x,y,u,v);
if (hf[u]!=hf[v]||hf[u]==0||hf[v]==0) printf("%d\n",dis[x]+dis[y]-2*dis[lp]);
else
{
if (u==v) printf("%d\n",abs(dis[x]-dis[y]));
else
{
int dx=dis[x]-dis[u],dy=dis[y]-dis[v],duv=abs(d[u]-d[v]);
printf("%d\n",min(duv,lg[hf[u]]-duv)+dx+dy);
}
}
}
}