【D20】虐场 allkill

【问题描述】

补觉完毕,查尔明开启了虐场模式,疯狂地屠掉前面那么多道逗逼题之后杀向了最后一
题。这一题是这样的:
给定一张有 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 行,每行包含一个整数,依次回答每个询问。

【样例输入】

5 5 3
1 2 3
2 3 2
3 4 5
1 4 6
5 1 3
3 5
1 2
1 3

【样例输出】

8
3
5

【数据规模和约定】
测试点编号 n m 约定
1 100 100 m=n
2 100 100 m=n
3 1000 1000 m=n-1
4 1000 1000 m=n-1
5 100000 100000 m=n-1且数据是一条链
6 100000 100000 m=n-1且数据是一条链
7 100000 100000 m=n-1
8 100000 100000 m=n-1
9 100000 100000 m=n
10 100000 100000 m=n

———————————————分割的线————————————————————————

【分析】

先讲一些拿前八十分的分类讨论算法
1~2组数据可以用floyd算法,三行代码完成O(n^3)复杂度的任意两点间最短路。
3~4、7~8组数据由于是一棵树,所以可以通过树上求LCA完成最短路。
5~6两组数据由于是链,可以定义一个链首和链尾,通过前缀和来维护最短路。

下方是正解想法
所有的图都可以看做是树或是树加一条边。
所以可以进行分类如果是所有的树,直接用树上LCA做。
防止一条链的爆站情况,可以用手工栈,需要掌握递归转非递归的思想,不然也可以使用上述提到的前缀和方案。
如果是树上加一条边,我们可以进行分类讨论,LCA不经过这一条边,或者LCA必经过这一条边。具体如下图:
分类讨论

如此,请看代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=101000;
struct node
{
    int v,nxt,c;
}edge[maxn<<1];
int head[maxn],tot=0;
void add_edge(int x,int y,int c)
{
    edge[tot].nxt=head[x];
    edge[tot].v=y;
    edge[tot].c=c;
    head[x]=tot++;
    return ;
}
int dist[200][200];
int n,m,q,s,large;  
int val[maxn];
int d[maxn],p[maxn][20];
struct f
{
    int u,dep;
    int ch;
}sp[maxn];
void sgz(int u,int dep)
{//手工栈非递归
    int top=1;
    sp[top].u=u,sp[top].dep=dep;
    sp[top].ch=head[u];
    while(top>0)
    {
        f &cur=sp[top];
        int v=edge[cur.ch].v;
        if(cur.ch==head[cur.u])
            d[cur.u]=cur.dep;
        if(cur.ch==-1)
        {
            top--;
            continue;
        }

        if(!d[v])
        {
            top++;
            sp[top].ch=head[v];
            sp[top].u=v;
            sp[top].dep=cur.dep+1;
            p[v][0]=cur.u;
            val[v]=val[cur.u]+edge[cur.ch].c;
        }
        cur.ch=edge[cur.ch].nxt;
    }
    return ;
}
void Init()
{
    s=1;
    p[s][0]=s;
    val[s]=0;
    sgz(s,1);
    for(int j=1;j<=19;j++)
        for(int i=1;i<=n;i++)
            p[i][j]=p[p[i][j-1]][j-1];
    return ;
}
int lca(int x,int y)
{
    if(d[x]>d[y]) swap(x,y);
    for(int j=19;j>=0;j--)
        if(d[y]-(1<<j)>=d[x]) y=p[y][j];
    if(x==y) return x;
    for(int j=19;j>=0;j--)
        if(p[x][j]!=p[y][j])
        {
            x=p[x][j];
            y=p[y][j];
        }
    return p[x][0];
}
int f[maxn];
int find(int x)
{
    if(f[x]!=x) return find(f[x]);
    return f[x];
}
int query(int x,int y)
{
    return val[x]+val[y]-2*val[lca(x,y)];
}
int main()
{
    freopen("allkill.in","r",stdin);
    freopen("allkill.out","w",stdout);
    memset(head,-1,sizeof(head));
    int fr,to,ww;
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        if(find(x)==find(y))
        {
            fr=x,to=y,ww=c;
        }
        else
        {
            f[f[x]]=f[y];
            add_edge(x,y,c);
            add_edge(y,x,c);
        }
    }
    Init();
    for(int i=1;i<=q;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(n!=m)printf("%d\n",query(x,y));
        else printf("%d\n",min(min(query(x,y),query(x,fr)+ww+query(to,y)),query(x,to)+ww+query(fr,y)));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/80558239