【思维】复杂度均摊+并查集——icpc cerc 2019 Saba1000kg

/*
复杂度均摊:
如果M>sqrt(N),这种询问最多sqrt(N)组,直接把所有边扫一遍求联通块即可
如果M<sqrt(N),直接两两枚举点判边,复杂度M^2,总复杂度N/M*M^2=NM 
*/
#include<bits/stdc++.h>
using namespace std;
#define N 100005 

vector<pair<int,int>>e;
set<pair<int,int> >s;

int n,E,P,a[N],F[N],id[N];
int find(int x){
    return F[x]==x?x:F[x]=find(F[x]);
}


int main(){
    //freopen("140.in","r",stdin);
    cin>>n>>E>>P;
    for(int i=1;i<=E;i++){
        int u,v;scanf("%d%d",&u,&v);
        if(u>v)swap(u,v);
        e.push_back(make_pair(u,v));
        s.insert(make_pair(u,v));
    }
    while(P--){
        int m;scanf("%d",&m);
        for(int i=1;i<=m;i++)scanf("%d",&a[i]);
        sort(a+1,a+1+m);
        for(int i=1;i<=m;i++)F[i]=i,id[a[i]]=i;
        if(m<=sqrt(n)){
            for(int i=1;i<=m;i++)
                for(int j=i+1;j<=m;j++)
                    if(s.find(make_pair(a[i],a[j]))!=s.end()){
                        int fu=find(i),fv=find(j);
                        if(fu!=fv)F[fu]=fv;
                    }
        }else {
            for(auto p:e)if(id[p.first] && id[p.second]){
                int fu=find(id[p.first]),fv=find(id[p.second]);
                if(fu!=fv)F[fu]=fv;
            }
        }
        int ans=0;
        for(int i=1;i<=m;i++)if(F[i]==i)ans++;
        for(int i=1;i<=m;i++)id[a[i]]=0;
        cout<<ans<<'\n';
    }
}
 

 

posted on 2020-05-19 10:39  zsben  阅读(200)  评论(0编辑  收藏  举报

导航