ICPC Central Europe Regional Contest 2019 Saba1000kg(複雜度均攤-並查集)

 

題目大意:

n個點,m條邊,q個詢問node

每次給出k個能用的點,把不能用的點的鏈接的邊刪除後問有幾個聯通塊ide

(k[i]的和小於n)spa

 

題目思路:

好妙啊~~~,還有點卡常code

首先考慮暴力作法blog

一號暴力選手

對於每次詢問,若是咱們對給出的點兩兩合併it

假設一次給出sqrt(n)個點,複雜度最壞爲O(n*n*q) [這時候q=1]io

二號暴力選手

對於每次詢問,暴力跑一邊全部的邊,合併邊的兩個端點class

複雜度最壞O(m*q)sed

 

看到上面兩種暴力方法其實都是可行的方法

要想一個方法下降複雜度,咱們綜合上面兩種方法

題目說k[i]的和小於n

 

因此

咱們對於大於sqrt(n)的k[i]採起二號暴力選手的作法

對於小於等於sqrt(n)的k[i]採起一號暴力選手的作法

 

爲何能夠呢?

若是此時給出k(k<=sqrt(n))個點

一號選手的複雜度最壞爲(sqrt(n)*sqrt(n)*q)

可是這種詢問咱們最多進行sqrt(n)次

因此複雜度最壞爲O(n*sqrt(n))

 

若是此時給出k(k>sqrt(n))個點

咱們暴力跑全部的邊,這種詢問最多也是進行sqrt(n)次

複雜度最壞爲O(sqrt(n)*m)

 

 

code:

int head[maxn],cnt;
struct node {
    int u,v,w,next;
} e[maxn];
void add(int u,int v,int w) {
    e[cnt].u=u,e[cnt].v=v,e[cnt].w=w;
    e[cnt].next=head[u],head[u]=cnt++;
}
int n,m,qq,p[maxn],a[maxn],vis[maxn];
vector<int>s[maxn];
int find(int x) {
    if(p[x]==x) return x;
    return p[x] = find(p[x]);
}
void combine(int x,int y) {
    int dx = find(x);
    int dy = find(y);
    if(dx==dy) return ;
    p[dx] = dy;
}
int main() {
    mst(head,-1);
    n=read(),m=read(),qq=read();
    rep(i,1,n) p[i] = i;
    for(int i=1 ; i<=m ; i++) {
        int u,v,w;
        u=read(),v=read();
        add(u,v,1),add(v,u,1);
        s[u].push_back(v);
        s[v].push_back(u);
    }
    rep(i,1,n) sort(s[i].begin(),s[i].end());

    while(qq--) {
        int temp=0;
        int k = read();
        int t = sqrt(n) ;
        for(int i=1 ; i<=k ; i++) {
            a[i] = read();
            vis[a[i]]=1;
            p[a[i]] = a[i];
        }
        if(k<t) {
            for(int i=1 ; i<=k ; i++ ) {
                for(int j=1 ; j<=k ; j++) {
                    auto it = lower_bound(s[a[i]].begin(),s[a[i]].end(),a[j]);
                    if(it==s[a[i]].end()||*it!=a[j]) continue;
                    combine(a[i],a[j]);
                }
            }
        } else {
            for(int i= 0; i<cnt; i++) {
                int u = e[i].u;
                int v = e[i].v;
                if(!vis[u]||!vis[v]) continue;
                combine(u,v);
            }
        }
        for(int i=1 ; i<=k ; i++) if(find(a[i])==a[i]) temp++;
        for(int i=1 ; i<=k ; i++) vis[a[i]] = 0;
        out(temp);
        puts("");
    }
    return 0;
}
View Code
相關文章
相關標籤/搜索