【BZOJ】1015 星球大战

【解析】排序+并查集

[分析]

求联通块,每操作一次询问一次。

搜索? O(N^2),TLE。

并查集,根据上次的结果更新答案。

问题在于,这是分离的操作,而不是合并的,怎么办呢?

反过来不就是合并的了吗?

给每个点标号,表示它打击的先后次序,例如结点3是第2个打击的,level[3]=2。

对于所有未打击的点,标号为打击的个数nl+1。

然后对于每条边,它一定是在第edge[i].w=min(level[edge[i].u]],level[edge[i].v])次打击不存在的。

根据w值从大到小排序。

然后动态维护res,反过来搞。

初始时res=n-nl,然后每次操作由于多了一个点,res++,然后考虑这个节点连通的情况,如果有连通则res--。

最后反过来输出答案。

[小结]

①对于每次操作求连通块个数的问题,通常采用并查集。

②对于动态分离,合并的问题,通常采用并查集。

③并查集的两个作用:合并,查找。

[代码]

/**************************************************************
    Problem: 1015
    User: y20070316
    Language: C++
    Result: Accepted
    Time:912 ms
    Memory:7840 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int N=400001;
const int M=200001;

struct E
{
    int u,v,w;
}edge[M];
int n,m;
int f[N],res[N];
int level[N],nl;

inline int read(void)
{
    int s=0,t=1; char c=getchar();
    for (;c<'0'||c>'9';c=getchar()) if (c=='-') t=-1;
    for (;'0'<=c&&c<='9';c=getchar()) s=(s<<1)+(s<<3)+c-'0';
    return s*t;
}

inline int min(int i,int j)
{
    return i<j?i:j;
}

inline int cmp(E ea,E eb)
{
    return ea.w>eb.w;
}

int find(int i)
{
    return f[i]==i?i:f[i]=find(f[i]);
}

int main(void)
{
    int x;
    n=read(),m=read();
    for (int i=1;i<=m;i++)
    {
        edge[i].u=read(); edge[i].u++;
        edge[i].v=read(); edge[i].v++;
    }
    nl=read();
    for (int i=1;i<=nl;i++) x=read(),level[++x]=i;
    for (int i=1;i<=n;i++)
        if (!level[i]) level[i]=nl+1;

    for (int i=1;i<=m;i++)
        edge[i].w=min(level[edge[i].u],level[edge[i].v]);
    sort(edge+1,edge+m+1,cmp);

    int j=0,fu,fv;
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=nl+1;i;i--)
    {
        res[i]=(i==nl+1?n-nl:res[i+1]+1);
        for (;j<m&&edge[j+1].w==i;)
        {
            fu=find(edge[++j].u);
            fv=find(edge[j].v);
            if (fu^fv) f[fu]=fv,res[i]--;
        }
    }
    for (int i=1;i<=nl+1;i++) printf("%d\n",res[i]);

    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-17 21:37:55

【BZOJ】1015 星球大战的相关文章

BZOJ 1015 星球大战

Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重新造出了他的超级武器.凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球.由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来.现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,

BZOJ 1015 星球大战 并查集+离线

这道题说来真是艰辛,从一开始的RE,到RE,到刚刚的WA,再到AC. 这只能说明我进步的历程,还有我需要不断的加强努力.这道题思路不难,从很久前在黑书中并查集一节就能找到解题的踪影,因为并查集只能并,分不了,所以我们就得离线,倒过来写.只不过这道题真的得审好题目,它问的是剩下的星球中有多少个连通分量,不要搞错了.大概就是这个样子了,加油. 1 #include<cstdio> 2 #include<iostream> 3 #define rep(i,j,k) for(int i=

bzoj 1015 星球大战starwar

题目大意: 一个n个点m条边的无向图,现在依次删除k个点及它们的边 求开始有几个联通块及每次删除一个点后的联通块 思路: 直接做不太好做 所以我们可以把删除离线下来 先把不被删的点之间都连起来,联通块个数即为最后一次的输出 然后倒序加入每个被删的点 再倒序输出即可 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstd

【BZOJ 1015】[JSOI2008]星球大战starwar

1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 3685  Solved: 1641[Submit][Status][Discuss] Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重

[BZOJ 1015] [JSOI 2008] 星球大战starwar

1015: [JSOI2008]星球大战starwar Time Limit: 3 SecMemory Limit: 162 MB Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重新造出了他的超级武器.凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球.由于星球的不断被摧毁,两

BZOJ 1015: [JSOI2008]星球大战starwar 并查集

1015: [JSOI2008]星球大战starwar Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重新造出了他的超级武器.凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球.由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来.现在,反抗军首领交给你一个任务:给出原

BZOJ 1015 JSOI2008 星球大战 starwar 并检查集合

标题效果:给定一个无向图.联通谋求块的数目,以及k一个点的破坏后每次:联通,块的数目 侧面和摧毁的地步全记录,我们可以做相反的. 需要注意的是该点不能算作破坏联通块 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 400400 using namespace std; struct abcd{ int to,next; }table[M];

BZOJ 1015 JSOI2008 星球大战

题目的关键点在于读入后,倒着进行并查集操作.因为并查集不支持删点操作,所以读入后.按照顺序,逆着操作就可以了. #include <cstdio> #include <vector> #include <algorithm> using namespace std;   vector<int>Edge[400005]; int x,y,n,m; int k,ban[200005]; int fa[400005]; bool vis[400005]; int

BZOJ 1015 JSOI2008 星球大战 starwar 并查集

题目大意:给定一个无向图,求联通块个数,以及k次每次摧毁一个点后的:联通块个数 将边和摧毁的点全记录下来,反着做即可. 注意被摧毁的点不能算作联通块 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 400400 using namespace std; struct abcd{ int to,next; }table[M]; int hea