luogu P1197 [JSOI2008]星球大战 并查集 逆向思维 邻接表

题目

刚开始做的时候每一次摧毁都初始化一次 毫无悬念的tle 复杂度O(k*(n+m))

后来瞄了一眼题解 把正向的摧毁改成反向的建立 结果还是tle 复杂度O(k*m)

再仔细看题解才发现不能用

father[i]==i
来判读联通块的数量

应该要用邻接表把所有的边都存起来 然后再建立星球的时候先加答案 然后再扫一遍所有的边 看能不能把两个不同联通块连在一起


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#define test cout<<"*"<<endl;
using namespace std;
const int N=1000000;
vector<int>g[N];
int father[N],dis[N],a[N],n,m;
void init()
{
    for(int i=0;i<=n;i++)
    {
        father[i]=i;
    }
}
int Find(int x)
{
    if(x==father[x])return x;
    father[x]=Find(father[x]);
    return father[x];
}
bool unite(int x,int y)
{
    int tx=Find(x);
    int ty=Find(y);
    if(tx==ty)return 0;
    father[ty]=tx;
    return 1;
}
int b[N],ans[N];
int tj()//联通块的数目
{
    int sum=0;
    for(int i=0;i<n;i++)
    {
        if(!b[i]&&father[i]==i)
        {
            sum++;
        }
    }
    return sum;
}
int main()
{
    int k,x,y;
    scanf("%d%d",&n,&m);
    init();
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    scanf("%d",&k);
    for(int i=0;i<k;i++)
    {
        scanf("%d",&a[i]);
        b[a[i]]=1;
    }
    for(int i=0;i<n;i++)
        if(!b[i])
            for(int j=0;j<g[i].size();j++)
                if(!b[g[i][j]])
                    unite(i,g[i][j]);
    ans[k]=tj();
    for(int i=k-1;i>=0;i--)
    {
        b[a[i]]=0;
        ans[i]=ans[i+1]+1;//多了一个点 先加一
        for(int j=0;j<g[a[i]].size();j++)
        {
            if(b[g[a[i]][j]])continue;
            if(unite(g[a[i]][j],a[i]))
                ans[i]--;
        }
//        cout<<"i="<<i<<" ans[i]="<<ans[i]<<endl;
    }
    for(int i=0;i<=k;i++)
        printf("%d\n",ans[i]);
    return 0;
}


猜你喜欢

转载自blog.csdn.net/yyyccww/article/details/78783343