首页 > 试题广场 >

隐匿社交网络

[编程题]隐匿社交网络
  • 热度指数:3014 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 1024M,其他语言2048M
  • 算法知识视频讲解
\hspace{15pt}牛客网有一个名为牛爱网神秘的入口。这天,牛可乐正在策划牛爱网的全新社交活动。
\hspace{15pt}每个人的账号在初始时都会被分配一个权重,第 i 个账号的权重为 w_i。对于任意的两个账号 ij,如果权重满足 (w_i \operatorname{and} w_j) \geqq 1,那么就会被分配到同一个社交网络。
\hspace{15pt}现在,牛可乐已经为 n 个账号分配了权重,他想知道,包含账号数量最多的社交网络中,包含多少个账号。
\hspace{15pt}牛爱网的日常维护工作忙坏了牛可乐,请你帮帮他。

\hspace{15pt}其中,\operatorname{and} 表示按位与运算。如果您需要更多位运算相关的知识,可以参考 OI-Wiki的相关章节

输入描述:
\hspace{15pt}每个测试文件均包含多组测试数据。第一行输入一个整数 T\left(1\leqq T\leqq 10^5\right) 代表数据组数,每组测试数据描述如下:

\hspace{15pt}第一行输入一个整数 n \left(1 \leqq n \leqq 10^5\right) 代表账号数量。
\hspace{15pt}第二行输入 n 个整数 w_1, w_2, \dots, w_n \left(1 \leqq w_i \leqq 10^{18}\right) 代表账号权重。

\hspace{15pt}除此之外,保证单个测试文件的 n 之和不超过 10^5


输出描述:
\hspace{15pt}对于每组测试数据,新起一行。输出一个整数,代表包含账号数量最多的社交网络中,包含的账号数量。
示例1

输入

2
5
2 1 6 7 16
2
2 16

输出

4
1

说明

\hspace{15pt}对于第一组测试数据,连接示意图如下图所示:
社交网络
import sys
from collections import defaultdict
input = sys.stdin.readline

class DSU:
    def __init__(self, n):
        self.parent = list(range(n))
        self.size = [1]*n

    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        fx, fy = self.find(x), self.find(y)
        if fx == fy: return
        if self.size[fx] < self.size[fy]:
            fx, fy = fy, fx
        self.parent[fy] = fx
        self.size[fx] += self.size[fy]

def get_set_bits(x):
    pos = 0
    while x:
        if x & 1:
            yield pos
        x >>= 1
        pos += 1

T = int(input())
for _ in range(T):
    n = int(input())
    w = list(map(int, input().split()))
    dsu = DSU(n)

    bit_map = defaultdict(list)
    zero_count = 0
    for i in range(n):
        if w[i] == 0:
            zero_count += 1
            continue
        for b in get_set_bits(w[i]):
            bit_map[b].append(i)

    for accounts in bit_map.values():
        root = accounts[0]
        for acc in accounts[1:]:
            dsu.union(root, acc)

    max_group = 0
    for i in range(n):
        if w[i] != 0 and dsu.find(i) == i:
            max_group = max(max_group, dsu.size[i])

    print(max(max_group, zero_count))

发表于 2025-07-28 22:40:17 回复(0)
java实现
import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        int t = in.nextInt();
        for(int i = 0; i < t; ++i) {
            int n = in.nextInt();
            int maxCount = 1;
            long[] weights = new long[n];
            Map<Long, Integer> nets = new HashMap<>();

            for(int j = 0; j < n; ++j) {
                weights[j] = in.nextLong();
            }
            nets.put(weights[0], 1);
            for(int j = 1; j < n; ++j) {
                long newWeight = weights[j];
                int newCount = 1;
                List<Long> oldWeights = new ArrayList<>();
                for(Map.Entry<Long, Integer> net : nets.entrySet()) {
                    if((weights[j] & net.getKey()) != 0) {
                        newWeight |= net.getKey();
                        newCount += net.getValue();
                        oldWeights.add(net.getKey());
                    }
                }
                for(int k = 0; k < oldWeights.size(); ++k) {
                    nets.remove(oldWeights.get(k));
                }
                nets.put(newWeight, newCount);
                maxCount = Math.max(maxCount, newCount);
            }
            System.out.println(maxCount);
        }
    }
}

发表于 2025-07-28 18:51:53 回复(0)
状态压缩,用个map存key是状态value是网络成员数量。
个元素x输入后遍历map,(x & key) >= 1就加入到这个网络,注意把原来的网络删掉插入新的进去,也就是插入{key | x, map[key] + 1}
如果(x & key) < 1就自建网络,即把{x, 1}加到map
处理完该组所有输入后要把map合并,map元素数量不会超过20个,遍历然后两两合并即可。
最后再遍历map输出最大的value
发表于 2025-07-08 18:50:14 回复(0)
并查集,权值进行或运算可以得知在哪个网络(网络大概有64个,因为1e18 < 2^64)中,记录每个网络中拥有的权值,然后根据权值合并网络,最后统计去重后网络账号数量的最大值
#include <bits/stdc++.h>
using namespace std;

vector<long long> father(64);

int Find(int u) {
    if (father[u] == u) return u;
    father[u] = Find(father[u]);
    return father[u];
}

void Union(int a, int b) {
    a = Find(a);
    b = Find(b);
    if (a == b) return;
    else father[a] = b;
}

int main() {
    long long n;
    cin >> n;
    while (n--) {
        long long m, ans = 0;
        cin >> m;

        vector<vector<int>> index(64);
        vector<long long> w(m);
        for (int i = 0; i < 64; i++) {
            father[i] = i;
        }

        for (int i = 0; i < m; i++) {
            cin >> w[i];
            vector<long long> temp;
            
            for (int j = 0; j < 64; j++) {
                if ((w[i] >> j) & 1) {
                    index[j].push_back(i);
                    temp.push_back(j);
                }
            }
            
            if (temp.size() > 1) {
                for (int i = 1; i < temp.size(); i++) {
                    Union(temp[i - 1], temp[i]);
                }
            }
        }

        for (int i = 0; i < 64; i++) {
            if (father[i] != i) {
                for (int j = 0; j < index[i].size(); j++) {
                    index[father[i]].push_back(index[i][j]);
                }
                sort(index[father[i]].begin(), index[father[i]].end());
            }
        }

        for (int i = 0; i < 64; i++) {
            long long cnt = 1;
            for (int j = 1; j < index[i].size(); j++) {
                if (index[i][j] != index[i][j - 1]) {
                    cnt++;
                }
            }
            ans = max(ans, cnt);
        }

        cout << ans << endl;
    }

    return 0;
}


发表于 2025-12-02 17:26:52 回复(0)
参考别人的思路写了一个版本。打算使用并查集再写一个版本

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {

    /**
     * 合并社交圈,计算最大社交圈的大小
     * 使用位运算表示用户的兴趣标签,通过或运算合并有共同兴趣的社交圈
     * 
     * @param interests 每个用户的兴趣标签数组(用long的二进制位表示)
     * @param userCount 用户数量
     */
    public static void mergeSocialCircles(long[] interests, int userCount) {
        // 使用Map表示社交圈:key为社交圈的标签(所有用户兴趣的或运算结果),value为该社交圈的用户数量
        Map<Long, Integer> socialCircles = new HashMap<>();
        int maxCircleSize = 0; // 记录最大社交圈的大小
        
        for (int i = 0; i < userCount; i++) {
            long currentInterest = interests[i];
            long mergedInterest = currentInterest; // 合并后的兴趣标签
            int mergedCount = 1; // 合并后的用户数量,初始为当前用户自己
            
            // 遍历现有社交圈,查找与当前用户有共同兴趣的圈子
            // 注意:这里使用临时数组来避免在遍历时修改Map
            Long[] existingCircles = socialCircles.keySet().toArray(new Long[0]);
            
            for (Long circleInterest : existingCircles) {
                // 检查当前用户兴趣与社交圈兴趣是否有交集(按位与结果不为0)
                if ((currentInterest & circleInterest) != 0) {
                    // 合并兴趣标签(按位或)
                    mergedInterest |= circleInterest;
                    // 累加用户数量
                    mergedCount += socialCircles.get(circleInterest);
                    // 移除被合并的社交圈
                    socialCircles.remove(circleInterest);
                }
            }
            
            // 将合并后的社交圈加入Map
            socialCircles.put(mergedInterest, mergedCount);
            // 更新最大社交圈大小
            maxCircleSize = Math.max(maxCircleSize, mergedCount);
        }
        
        System.out.println(maxCircleSize);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取测试用例数量
        int testCaseCount = scanner.nextInt();
        
        for (int i = 0; i < testCaseCount; i++) {
            // 读取当前测试用例的用户数量
            int userCount = scanner.nextInt();
            long[] interests = new long[userCount];
            
            // 读取每个用户的兴趣标签
            for (int j = 0; j < userCount; j++) {
                interests[j] = scanner.nextLong();
            }
            
            // 处理当前测试用例
            mergeSocialCircles(interests, userCount);
        }
        
        scanner.close();
    }
}


发表于 2025-09-10 11:14:51 回复(0)
密码的,这跟并查集有啥关系,思想是带着点并查集思想,但是用并查集那一套,find,union啥的,就是超时
发表于 2025-09-09 15:18:32 回复(0)
import java.util.*;

public class Main {
    static int[] parent;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();

        while (T-- > 0) {
            int n = sc.nextInt();
            long[] w = new long[n];

            for (int i = 0; i < n; i++) {
                w[i] = sc.nextLong();
            }

            // 初始化并查集
            parent = new int[n];
            for (int i = 0; i < n; i++) parent[i] = i;

            // 建立每一位bit(最多60位)到有哪些节点的映射
            List<Integer>[] bitToNodes = new List[60];
            for (int i = 0; i < 60; i++) {
                bitToNodes[i] = new ArrayList<>();
            }

            for (int i = 0; i < n; i++) {
                for (int b = 0; b < 60; b++) {
                    if (((w[i] >> b) & 1) == 1) {
                        bitToNodes[b].add(i);
                    }
                }
            }

            // 对每一位,将所有该位为1的点合并
            for (int b = 0; b < 60; b++) {
                List<Integer> list = bitToNodes[b];
                for (int i = 1; i < list.size(); i++) {
                    union(list.get(0), list.get(i));
                }
            }

            // 统计连通块大小
            Map<Integer, Integer> count = new HashMap<>();
            int max = 0;
            for (int i = 0; i < n; i++) {
                int root = find(i);
                count.put(root, count.getOrDefault(root, 0) + 1);
                max = Math.max(max, count.get(root));
            }

            System.out.println(max);
        }
    }

    static int find(int x) {
        if (parent[x] != x) parent[x] = find(parent[x]);
        return parent[x];
    }

    static void union(int x, int y) {
        int fx = find(x);
        int fy = find(y);
        if (fx != fy) parent[fy] = fx;
    }
}
发表于 2025-07-27 21:03:35 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int T;
    cin>>T;
    while(T--){
     int n,maxnum=0,temp;
    cin>>n;
    vector<bitset<64>> acc(n);
    bitset<64> bve;
    for(int i=0;i<n;i++){
        cin>>temp;
        acc[i]=temp;     
    }
    for(int i=0;i<n;i++){
        int count=1;
        bve =acc[i];
        for(int j=i+1;j<n;j++){
            if((bve & acc[j])!=0) {
            bve |= acc[j];    
            count++;
            swap(acc[++i],acc[j]);
            j=i;
            }
        }
        maxnum=max(count,maxnum);
    }
   cout<<maxnum<<endl;
    }
}
发表于 2025-07-10 12:50:30 回复(0)
// 用例不通过。实在看不出问题在哪
#include <iostream>
#include <vector>
using namespace std;


void calConnected(const vector<long long>& data, vector<vector<bool>>& connected)
{
    int n=data.size();
    connected.resize(n, vector<bool>(n, false));

    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            connected[i][j] = data[i]&data[j];
        }
    }
}

long long getAccountNum(vector<long long>& w, int x, vector<vector<bool>>& connected)
{
    long long account_num = 1;
    for(int i=0; i<w.size(); i++)
    {
        if(i!=x && connected[i][x])
            account_num++;
    }
    return account_num;
}


long long getMaxAccountNum(vector<long long>& w, vector<vector<bool>>& connected)
{
    int n = w.size();
    // vector<bool> visited(n, false);
    long long max_account_num = 0;
    for(int i=0; i<w.size(); i++)
    {
        // int cur_account_num = getAccountNum(w, i, connected);
        max_account_num = max(max_account_num, getAccountNum(w, i, connected));
    }
    return max_account_num;
}


int main() {
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        vector<long long> weight(n);
        for(long long& x:weight)
            cin>>x;

        //题目的空间限制很宽泛。这里预处理任意两个账号的权重
        vector<vector<bool>> connected;
        calConnected(weight, connected);

        long long max_account_num = getMaxAccountNum(weight, connected);
        cout<<max_account_num<<endl;
    }
    
    return 0;
}
// 64 位输出请用 printf("%lld")

发表于 2025-06-14 18:59:53 回复(0)