题解 P8250 交友问题
题目传送门
前置知识:
根号分治。
简要题意:
- 给定一个
n 个点m 条边的无向图,q 次询问,每次询问给出两个点u,v ,询问有多少与u 相邻的点不与v 相邻也不是v 本身。 -
分析:
记点
记询问
考虑暴力。对于单次询问,找出所有与
memset(vis, 0, sizeof vis);
for(int z: ver[u]) {
vis[z]=1;
}
int ans=vis[v];
for(int z: ver[v]) {
ans+=vis[z];
}
printf("%d\n", deg[u]-ans);
单次询问更优的实现(对标记“懒”清空)可以做到
考虑优化。如果有两个询问完全相同,则不重复计算;即对询问去重。同时,对于同一个
上述过程中我们可以处理
这个上界对所有的正实数
需要注意的是,这一步的优化保证了
由于对两种点的做法是一样的,所以代码比较简单。因为用了快排去重,所以代码的复杂度是
#include <bits/stdc++.h>
#define rep(i, l, r) for(int i=l, _=r; i<=_; ++i)
using namespace std;
typedef long long ll;
const int mN=2e5+9, mM=7e5+9;
int n, m, q, deg[mN];
vector<int> ver[mN];
int ans[mN];
vector<pair<int, int> > qn[mN];
int vis[mN];
int main() {
scanf("%d%d%d", &n, &m, &q);
rep(i, 1, m) {
int x, y;
scanf("%d%d", &x, &y);
++deg[x], ++deg[y];
ver[x].push_back(y), ver[y].push_back(x);
}
rep(i, 1, q) {
int x, y;
scanf("%d%d", &x, &y);
ans[i]=deg[x]; //ans 的剩余部分(deg[x]-g(x, y))中,x y 地位相等
if(deg[x]<deg[y]) swap(x, y); //保证 x 的 deg 更大
qn[x].push_back({y, i});
}
rep(x, 1, n) {
sort(qn[x].begin(), qn[x].end()); //为了去重
for(int y: ver[x]) {
vis[y]=x;
}
int lst=0, lstans=0;
//lst 记录上一个询问的 y, lstans 记录上一个询问的 deg[x]-g(x, y)
for(auto ask: qn[x]) {
const int y=ask.first, id=ask.second;
if(y==lst) {
ans[id]-=lstans;
continue;
}
lstans=vis[y]==x;
for(int z: ver[y]) {
lstans+=vis[z]==x;
}
ans[id]-=lstans;
lst=y;
}
}
rep(i, 1, q) printf("%d\n", ans[i]);
return 0;
}