统计点对数目+容斥原理(47双周赛)

统计点对数目(容斥原理)

题目:

给你一个无向图,无向图由整数 n  ,表示图中节点的数目,和 edges 组成,其中 edges[i] = [ui, vi] 表示 ui 和 vi 之间有一条无向边。同时给你一个代表查询的整数数组 queries 。

第 j 个查询的答案是满足如下条件的点对 (a, b) 的数目:

1.a < b
2.cnt 是与 a 或者 b 相连的边的数目,且 cnt 严格大于 queries[j] 。
请你返回一个数组 answers ,其中 answers.length == queries.length 且 answers[j] 是第 j 个查询的答案。

请注意,图中可能会有 重复边 。

1.将这条边映射到整数 pmax*(n+1)+pmin。其中,要首先取最大值和最小值的原因在于,需要处理重边(如 (1,2)(1,2) 和 (2,1)(2,1) 的情况。)

class Solution {
public:
    vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
        vector<int> deg(n + 1, 0);                        //点的度的集合
        int nEdges = edges.size();
        unordered_map<int, int> overlap;                    
        // a,b同时相连的边的个数
        //这里的hashmap不是键值对,而是为了节省空间而建的,两个点之间边的数量(题目说可能有重边)
        vector<vector<int>> distinctEdges; // 去除重边后的边数组,这样处理每个 query 时能少遍历几条边
        auto encode = [n](int a, int b) -> int {return max(a, b) * (n + 1) + min(a, b);};                    //为hashmap设置hash值
        for (int i = 0; i < nEdges; i++) {
            int p = edges[i][0], q = edges[i][1];        //pq之间的边
            deg[p]++;                                    //两边度数增加
            deg[q]++;
            int idx = encode(p, q);                      //取得这条边的hash码
            if (overlap.find(idx) == overlap.end()) {    
                distinctEdges.push_back({p, q});
            }//迭代器找遍了没找到,说明没有这条边,就可以加入不重复边集
            overlap[idx]++;
                                       
        }

        vector<int> sortedDeg(deg.begin() + 1, deg.end());    
        sort(sortedDeg.begin(), sortedDeg.end());            //按点的度排序
        
        int nQueries = queries.size();                        //查询序列长度
        vector<int> ret(nQueries);                            //转arraylist
        for (int i = 0; i < nQueries; i++) {
            int l = 0, r = n - 1;
            int cnt = 0;
            while (l < n) {
                    //这里的点的顺序不是原来的点的顺序,
                while (r > l && sortedDeg[l] + sortedDeg[r] > queries[i]) {
                    r--;                                      //与a和b相连的边数严格大于它
                }                                         //固定a,找满足条件的,度最小的b
                cnt += (n - max(l, r) - 1);                    //cnt是与a/b相连的最大边数
                l++;                                            
            }

            for (int j = 0; j < distinctEdges.size(); j++) {
                int p = distinctEdges[j][0], q = distinctEdges[j][1];
                int idx = encode(p, q);
                if (deg[p] + deg[q] > queries[i] && deg[p] + deg[q] - overlap[idx] <= queries[i]) {
                    cnt--;
                }
            }
            ret[i] = cnt;
        }
        return ret;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值