2023年牛客基础训练营2-K
题目链接:https://ac.nowcoder.com/acm/contest/46810/K
思路:从未见过的科技,根号分治。
用这个想法的原因:
本题暴力的主要瓶颈在于对菊花图的花芯的\(n\)次访问,最坏复杂度会变成\(n^2\)级别,所以我们将所有点按照度的多少分类,分类的界限为\((val = \sqrt{n})\),再将所有大于等于这个界限的点建图,查询的时候也分成两类:如果这个点的度数小于\(val\),则直接遍历,否则在新建立的图上遍历。无论哪种情况,遍历的边数一定小于\(val\)次,时间复杂度为\(n\sqrt{n}\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int h[N],ne[N*2],num[N*2],idx;
bool st[N],stt[N];
int cnt[N];
int id[N];
int du[N];
vector<int>g[N];
void add(int x,int y){
num[idx] = y,ne[idx] = h[x],h[x] = idx++;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n,m,q;
cin>>n>>m>>q;
memset(h,-1,sizeof(h));
for (int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
du[x]++,du[y]++;
}
int val = sqrt(n);
for (int i=1;i<=n;i++){
for (int j=h[i];j!=-1;j=ne[j]){
int e = num[j];
if (du[i]>=val&&du[e]>=val){
g[i].push_back(e);
}
}
}
while(q--){
int k;
cin>>k;
for (int i=1;i<=k;i++) {
int x;
cin>>x;
id[i] = x;
st[x] = 1;
if (du[x]>=val) stt[x] = 1;
}
int ans = 0;
for (int i=1;i<=k;i++){
if (du[id[i]]<val) st[id[i]] = 0;
else stt[id[i]] = 0;
if (du[id[i]]<val){
for (int j=h[id[i]];j!=-1;j=ne[j]){
int v = num[j];
if (st[v]) ans++;
}
}else{
for (auto v:g[id[i]]){
if (stt[v]) ans++;
}
}
}
for (int i=1;i<=k;i++){
st[id[i]] = 0;
}
cout<<ans<<'\n';
}
}
浙公网安备 33010602011771号