交友问题

交友问题

题目背景

题目描述

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

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

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

输入格式

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

接下来 m 行,每行两个数 xi,yi,表示用户 xiyi 之间有一条双向边。

接下来 q 行,每行两个数 ui,vi,表示第 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% 的数据,满足 1n,q2×1031m8×103

对于 60% 的数据,满足 1n,q2×1041m5×104

对于 100% 的数据,满足 1n,q2×1051m7×105

保证没有重边和自环

 

解题思路

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

  我们本质是想统计与 u 相邻但不与 v 相邻的节点,所以可以给每个点 i 开一个 std::bitset 来记录与它相邻的点,记为 bi。那么既与 uv 相邻的节点就是 bu&bv,则只与 u 相邻不与 v 相邻的节点就是 bu(bu&bv)(如果 uv 相邻需要将 bu 的第 v 位置为 0)。时间复杂度就是 O(qnw),时限 3s 是可以过的,但这种做法的空间复杂度为 O(n2) 会爆掉。

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

  接下来就是分类讨论。

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

  已知 O(qnw) 是可以过的,因此我们可以将两种暴力的时间复杂度统一起来,即令 blogn=nw,有 b=nwlogn。另外空间复杂度为 O(2mbnw)O(wmlogn),这就不会爆空间了。

  AC 代码如下,时间复杂度为 O(qnw)

#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 @   onlyblues  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示