[2016ICPC 大连网络预选赛] HDU5876 图论

题意

给一个无向图,求它的补图中,每个点到S点的最短路。输入点数N<=2*10^5,边数M<=2*10^4。

思路

原图边很少,所以补图边很多。那么有相当多的点最短路为1,而且最短路的长度也会很小。那么我们可以一层一层的求出最短路。先从S求出距离为1的点,即原图不存在(1,u)。然后再从距离为1的点集向距离未知的点集更新。更新的过程就是扫边表,如果有一个跨两个集合的原图边,就把未知点的cnt++。如果未知集合的某个点cnt加到了距离1集合的点数,那么它就不是距离为2的点。于是我们把未知集合分为距离为2的集合和距离位置的集合,再如法炮制,用距离为2的集合更新位置集合得到距离为3的集合。。。
这样的时间复杂度并不大,如果N>M那么所有的最短路都不超过1。最复杂的情况莫过于为补图构造一些或者很多很长的最短路,但是n个点完全图就有n(n-1)/2条边,要构造顶多长度也超不过200。而点数一少复杂度就很明显的下降了。

AC代码 C++

#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>

using namespace std;

#define MAXN 200022

vector<int> g[MAXN];
int q[MAXN << 1];
int tmp[MAXN];
int ans[MAXN];
int cnt[MAXN][2];

int main()
{
    int T, clock = 1, n, m, u, v, S, i, cur, step, hd, tl, TL, sz;
    set<int> st[2];
    set<int>::iterator it;
    scanf("%d", &T);
    while (T--)
    {
        memset(ans, -1, sizeof(ans));
        scanf("%d%d", &n, &m);
        st[0].clear();
        st[1].clear();
        for (i = 1; i <= n; i++)
        {
            st[0].insert(i);
            g[i].clear();
        }
        while (m-- && scanf("%d%d", &u, &v) > 0)
        {
            g[u].push_back(v);
            g[v].push_back(u);
        }
        scanf("%d", &S);
        st[0].erase(S);
        hd = tl = 0;
        q[tl++] = S;
        ans[S] = 0;
        clock++;
        for (cur = 0, step = 1, TL = 0; tl != TL; cur = 1 - cur, step++, clock++)
        {
            TL = tl;
            st[1 - cur].clear();
            sz = TL - hd;
            while (hd < TL)
                for (i = 0, u = q[hd++]; i < g[u].size(); i++)
                    if (ans[v = g[u][i]] == -1)
                        if (cnt[v][0] != clock)
                        {
                            cnt[v][0] = clock;
                            cnt[v][1] = 1;
                        }
                        else
                            cnt[v][1]++;
            for (it = st[cur].begin(), m = 0; it != st[cur].end(); tmp[m++] = *it++);
            for (i = 0, u = tmp[0]; i<m; u = tmp[++i])
                if (cnt[u][0] == clock && cnt[u][1] == sz)
                {
                    st[1 - cur].insert(u);
                    st[cur].erase(u);
                }
                else
                {
                    ans[u] = step;
                    q[tl++] = u;
                }
        }
        for (i = 1, cur = 0; i <= n; i++)
            if (i != S)
                tmp[cur++] = ans[i];
        for (i = 0; i < cur; i++)
            printf("%d%c", tmp[i], "\n "[i + 1 != cur]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值