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;
}
};
四、总结
主要是要注意思考那个关键的式子从哪里去优化,我们发现有大量的点对没有重复计算的必要。
老规矩:写不出的题即不写了,睡大觉,明天就写得出来了。