【LeetCode 1782】Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务

133 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务活动详情

一些唠叨

(2022.3.19)写了俩小时写麻了,留个坑,明天再写

(2022.3.20)花了十分钟就A了,事实再一次证明,搞不定的东西就睡大觉,第二天醒来再说。

一、题目描述

给定一张无向图m,再给定q个询问,每个询问有一个数字k,求图里有多少点对,满足与这两个点任意之一相连的边数大于k

数据范围

m <= 20000, q<=20

二、思路分析

一开始的想法:先记录一些每个点有多少个边与之相连,记为a[i],再暴力for一下所有点对(x,y),那么就是统计有多少点对满足 a[x]+a[y]-d(x,y) > k,其中d(x,y)表示连接(x,y)的边数,因为这些边被重复算了两次。a[i]好求,一遍for即可。d(x,y)就需要用到map,即map<pair<int, int>, int> mp;但是,问题就出现在了暴力for所有点对(x,y)上,一开始抱有侥幸心理觉得几个亿卡一卡应该能过去吧,结果实测不行,得老老实实想办法。

有没有什么办法可以不用for所有的点对呢?我们发现,我们的答案只可能出现在a[x]+a[y]的点对之中,只是会混入一些a[x]+a[y]-d(x,y)<=k 的点对,前者好算,sort一下,利用单调性for一遍就统计完了,后者也好算,因为有d(x,y)的存在,点对(x,y)一定只在我们的边里面,所以for一遍所有的边,排查出这些内鬼(a[x]+a[y]>k但是a[x]+a[y]-d(x,y)<=k)就好了。

问题就完美解决啦。

三、AC代码

class Solution {
public:

    map<pair<int, int>, int> mp;
    map<pair<int, int>, int>::iterator item,it;
    int a[200005],f[200005],b[200005];

    int js(vector<vector<int>>& edges, int q) {
        int m = edges.size(), ans=0;
        for (it = mp.begin(); it!=mp.end(); it++){
            int u = it->first.first;
            int v = it->first.second;
            if (u > v) swap(u,v);
            if (b[u]+b[v]>q && b[u]+b[v]-it->second <= q) {
                ans++;
            }
        }
        return ans;
    }

    vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
        int m = edges.size();
        for (int i=0; i<m; i++) {
            int u = edges[i][0];
            int v = edges[i][1];
            a[u]++;
            a[v]++;
            if (u > v) swap(u,v);
            item = mp.find(make_pair(u,v));
            if (item == mp.end()) {
                mp.insert(make_pair(make_pair(u,v), 1));
            } else {
                int t = item->second;
                mp.erase(item);
                mp.insert(make_pair(make_pair(u,v), ++t));
            }
        }
        for (int i=1; i<=n; i++) b[i] = a[i];
        sort(a+1,a+1+n);
        vector<int> result;
        for (int i=1; i<=n; i++) printf("%d ",a[i]);
        for (int i=0; i<queries.size(); i++) {
            int ans=0, l=0, r=n, q = queries[i];
            while (l+1 < r) {
                l++;
                while (a[l]+a[r] > q && l<r) r--;
                ans += n-r;
            }
            while (l<n) {
                l++;
                ans+=n-l;
            }
            ans-=js(edges, q);
            result.push_back(ans);
        }
        return result;
    }
};

四、总结

主要是要注意思考那个关键的式子从哪里去优化,我们发现有大量的点对没有重复计算的必要。

老规矩:写不出的题即不写了,睡大觉,明天就写得出来了。