问题描述
补觉完毕,查尔明开启了虐场模式,疯狂地屠掉前面那么多道逗逼题之后杀向了最后一
题。这一题是这样的:
给定一张有 n 个点 m 条边的无向图,每条边有边权,而且保证这张图是联通的。
现在有 q 次询问,每次询问会给出两个点 x,y,求 x 到 y 的最短路的长度。
不用多说,这也是一道逗逼题。查尔明分分钟就写好了总复杂度为 O(1)的标算,但是
他很担心自己是否手抖了,所以想请你写个程序来帮助对拍。当然对拍的程序也不能太慢,
因为查尔明非常喜欢用极限数据来跑对拍,这样才能体现出自己程序时间复杂度的优越性。
请你务必帮助查尔明完成对拍程序,这可是查尔明 AK 的最后一步!
输入格式
输入文件 allkill.in 第一行包含三个正整数 n,m,q,表示点数、边数和询问数。
接下来 m 行,每行包含三个正整数 u,v,w,表示 u 与 v 有一条边权为 w 的无向边。
接下来 q 行,每行包含两个正整数 x 和 y,表示一组询问。
输出格式
输出文件 allkill.out 包含 q 行,每行包含一个整数,依次回答每个询问。
题解
- 其实这道题不是很难,看数据的约定就知道要分类讨论
- 对于m=n-1 的情况分为两种,一种是纯粹的树,那么就是十分朴素的LCA,而对于退化的一条链来说,只需要线性的前缀和就好了(建树会爆栈),这里通过每个节点的度小于等于2来判断
- 而对于m=n情况,就需要拿掉多余的边,然后分别进行LCA取最小值就好了
ans=minn(get_sum(a,b),get_sum(a,ano.from)+get_sum(b,ano.to)+ano.v);
ans=minn(get_sum(b,ano.from)+get_sum(a,ano.to)+ano.v,ans);//ano是多余的一条边
附上st做法的LCA,倍增算法在最后两个点会超时(查询时间复杂度太高了qwq)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#define INF 0xfffffff
using namespace std;
void fff(){
freopen("allkill.in","r",stdin);
freopen("allkill.out","w",stdout);
}
struct edge{
int from,to,v;
edge(int from,int to,int v):from(from),to(to),v(v){}
edge(){
}
};
edge ano;
const int MAXN=100010;
int num[MAXN];
vector < edge > Edge;
vector < int > G[MAXN];
int n,mm,q;
bool visited[MAXN];
int ver[2*MAXN],R[2*MAXN],first[MAXN];
int tot=0;
void dfs(int u,int deepth){
visited[u]=true;
ver[++tot]=u;
first[u]=tot;R[tot]=deepth;
int size=G[u].size();
for (int i=0;i<size;i++){
int v=Edge[G[u][i]].to;
if(!visited[v]){
num[v]=num[u]+Edge[G[u][i]].v;
dfs(v,deepth+1);
ver[++tot]=u;
R[tot]=deepth;
}
}
}
int m[2*MAXN][22];
int min(int x,int y){
return (R[x]>R[y])? y:x;
}
int minn(int x,int y){
return (x>y)? y:x;
}
void st(){
for (int i=1;i<=tot;i++){
m[i][0]=i;
}
for (int j=1;(1<<j)<=tot;j++){
for (int i=1;i+(1<<j)-1<=tot;i++){
m[i][j]=min(m[i][j-1],m[i+(1<<(j-1))][j-1]);
}
}
}
int find(int i,int j){
if(i>j){
int temp=i;
i=j;
j=temp;
}
int k=log(j-i+1)/log(2);
return min(m[i][k],m[j-(1<<k)+1][k]);
}
void init(){
memset(ver,0,sizeof(ver));
memset(R,0,sizeof(R));
memset(first,0,sizeof(first));
memset(visited,false,sizeof(visited));
memset(m,0,sizeof(m));
}
int get_sum(int a,int b){
int fa=ver[find(first[a],first[b])];
return num[a]+num[b]-num[fa]*2;
}
int abs(int a){
if(a<0) return -a;
return a;
}
int main(){
fff();
bool falg=false;
memset(visited,false,sizeof(visited));
scanf("%d%d%d",&n,&mm,&q);
for (int i=1;i<=mm;i++){
int a,b,v;
scanf("%d%d%d",&a,&b,&v);
if(visited[a]&&visited[b]){
ano=(edge){a,b,v};
continue;
}
Edge.push_back((edge){a,b,v});
G[a].push_back(Edge.size()-1);
Edge.push_back((edge){b,a,v});
G[b].push_back(Edge.size()-1);
if(G[a].size()>2) falg=true;
visited[a]=true;
visited[b]=true;
}
init();
num[1]=0;
if(falg){
dfs(1,1);
st();
}else{
int u=1;
visited[1]=true;
while (n>1){
int v,w;
for (int i=0;i<G[u].size();i++){
int temp=Edge[G[u][i]].to;
if(!visited[temp]){
v=temp;
w=Edge[G[u][i]].v;
visited[temp]=true;
}
}
num[v]=num[u]+w;
u=v;
n--;
}
}
for (int i=1;i<=q;i++){
int a,b;
int ans=INF;
scanf("%d%d",&a,&b);
if(n==mm){
ans=minn(get_sum(a,b),get_sum(a,ano.from)+get_sum(b,ano.to)+ano.v);
ans=minn(get_sum(b,ano.from)+get_sum(a,ano.to)+ano.v,ans);
}else{
if(falg){
ans=get_sum(a,b);
}else{
ans=abs(num[a]-num[b]);
}
}
printf("%d\n",ans);
}
return 0;
}