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';
    }
}
posted @ 2023-04-10 18:44  安潇末痕  阅读(32)  评论(0)    收藏  举报