交友问题

交友问题

题目背景

题目描述

洛谷上有 $n$ 位用户,这些用户组成了一个双向的网络。

洛谷的图片分享机制如下:如果第 $i$ 个用户向他的好友 $j$ 分享了一张照片,那么,$j$ 的所有好友 $k$ 就能看到这张照片。$j$ 也可以看到这张照片

现在,用户 $u_i$ 想分享一张照片,但是TA不想让用户 $v_i$ 看到这张照片。在不发送给自己的情况下,TA想知道,他最多可以发送给多少位好友?

输入格式

第一行三个正整数 $n,m,q$,分别代表用户数,边数,询问数。

接下来 $m$ 行,每行两个数 $x_i,y_i$,表示用户 $x_i$ 与 $y_i$ 之间有一条双向边。

接下来 $q$ 行,每行两个数 $u_i,v_i$,表示第 $i$ 次询问。

输出格式

对于每一次询问,输出一行,包含一个数,答案。

样例 #1

样例输入 #1

6 7 8
5 1
1 4
1 6
5 6
5 4
1 2
5 3
5 3
1 1
3 6
1 5
5 6
1 4
5 2
2 6

样例输出 #1

3
0
0
1
2
2
3
0

提示

对于 $20\%$ 的数据,满足 $1 \le n,q \le 2\times10^3$,$1\le m \le 8\times 10^3$;

对于 $60\%$ 的数据,满足 $1 \le n,q \le 2\times10^4$,$1\le m \le 5\times 10^4$;

对于 $100\%$ 的数据,满足 $1 \le n,q \le 2\times10^5$,$1\le m \le 7\times 10^5$。

保证没有重边和自环

 

解题思路

  容易想到暴力的做法。对于询问 $(u, v)$,我们枚举 $u$ 的所有邻点 $x$,如果 $x \ne v$ 并且 $x$ 的邻点不含 $v$,则可以将图片发送给 $x$。如果用 std::set 来存储邻点,那么时间复杂度就是 $O(q \cdot n \log{n})$。

  我们本质是想统计与 $u$ 相邻但不与 $v$ 相邻的节点,所以可以给每个点 $i$ 开一个 std::bitset 来记录与它相邻的点,记为 $b_i$。那么既与 $u$ 和 $v$ 相邻的节点就是 $b_u \, \& \, b_v$,则只与 $u$ 相邻不与 $v$ 相邻的节点就是 $b_u \oplus (b_u \, \& \, b_v)$(如果 $u$ 和 $v$ 相邻需要将 $b_u$ 的第 $v$ 位置为 $0$)。时间复杂度就是 $O\left(q \cdot \frac{n}{w}\right)$,时限 $3s$ 是可以过的,但这种做法的空间复杂度为 $O \left( n^2 \right)$ 会爆掉。

  当我们想到两种暴力的做法,就可以尝试根号分治了。如果要在图论问题中用根号分治,一般是根据点的轻重进行分类。设置一个阈值 $b$,如果一个节点的度数不超过 $b$,则该节点定义为轻节点,否则节点的度数超过 $B$ 就定义为重节点。假设图中边的数量为 $m$,可以知道重节点的数量不超过 $\frac{2m}{b}$。

  接下来就是分类讨论。

  • 如果 $u$ 是轻节点,那么我们直接用第一种暴力,时间复杂度是 $O(b \log{n})$。
  • 否则如果 $v$ 是轻节点,我们也用第一种暴力,不过此时我们枚举的是 $v$ 的所有邻点 $x$。一开始先假设答案选择了 $u$ 的所有邻点,如果 $x = u$ 或者 $x$ 的邻点含 $u$,那么这个点不能被选,应将其从答案中删掉。时间复杂度也是 $O(b \log{n})$。
  • 否则,此时 $u$ 和 $v$ 都是重节点,直接用第二种暴力即可,时间复杂度是 $O\left(\frac{n}{w}\right)$。

  已知 $O\left(q \cdot \frac{n}{w}\right)$ 是可以过的,因此我们可以将两种暴力的时间复杂度统一起来,即令 $b \log{n} = \frac{n}{w}$,有 $b = \frac{n}{w \log{n}}$。另外空间复杂度为 $O\left(\frac{2m}{b} \cdot \frac{n}{w}\right) \approx O\left(w \cdot m \log{n}\right)$,这就不会爆空间了。

  AC 代码如下,时间复杂度为 $O\left(q \cdot \frac{n}{w}\right)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 5, B = 180;

set<int> st[N];

int main() {
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    while (m--) {
        int u, v;
        scanf("%d %d", &u, &v);
        st[u].insert(v);
        st[v].insert(u);
    }
    map<int, bitset<N>> mp;
    for (int i = 1; i <= n; i++) {
        if (st[i].size() > B) {
            for (auto &x : st[i]) {
                mp[i][x] = 1;
            }
        }
    }
    while (k--) {
        int u, v;
        scanf("%d %d", &u, &v);
        int ret = 0;
        if (st[u].size() <= B) {
            for (auto &x : st[u]) {
                if (x != v && !st[x].count(v)) ret++;
            }
        }
        else if (st[v].size() <= B) {
            ret = st[u].size();
            for (auto &x : st[v]) {
                if (x == u || st[x].count(u)) ret--;
            }
        }
        else {
            ret = (mp[u] ^ (mp[u] & mp[v])).count() - mp[u][v];
        }
        printf("%d\n", ret);
    }
    
    return 0;
}

 

参考资料

  暴力美学——浅谈根号分治:https://www.luogu.com/article/5gtqzd4a

  P8250 题解:要平衡的是空间,而不是时间:https://www.luogu.com.cn/article/wbg93nq4

posted @ 2024-04-24 20:06  onlyblues  阅读(5)  评论(0编辑  收藏  举报
Web Analytics