交友问题
交友问题
题目背景
题目描述
洛谷上有 $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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18156213