LGOJ P1197 【[JSOI2008]星球大战】

我十分喜欢自带大常数的STL

思路都是反着来,先把该删的节点都删除,再一个一个往图里面加。
加节点的时候分类讨论:

假设当前要加的节点是 u ,用

set<int> S;

来存储 u 节点直接连接的点 v 的所属集合的值 find(v) ,这样 S.size() 的值就表示 u 节点究竟链接了几种不同的联通块。再分类讨论:

if (S.size() == 0)tpc++;//若这是一个单独的点
                        //添加之后tpc++
else if (S.size() == 1);//若这个点只连接了一个联通块
						//那么她的存在就没有什么所谓
else
{
	tpc -= S.size();    //如果这个点连接了两个以上的块
    					//那么显然tpc要减去(S.size()-1)
	tpc++;
}

注:tpc == type_count.

剩下的事情就简单了,先dfs一次求末状态的块数量,链表存贮 k 次操作,倒着加点,再倒着输出即可。


// P1197.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	char c = getchar();
	int x = 0;
	bool minus = 0;
	for (; !isdigit(c); c = getchar())
		if (c == '-')minus = 1;
	for (; isdigit(c); c = getchar())
		x = x * 10 + c - '0';
	if (minus)return -x;
	return x;
}
const int M = 200005, N = 400005;
int n, m, k, f[N], tpc;
vector<int> G[N];//Graph
list<int> atk;//attack_list
bool ban[N];//值为1表示当前节点不可用
bool vis[N];//dfs用的数组
list<int>asl;//answer_list
set<int>S;

//普通的并查集
inline int find(int x)
{
	if (f[x] == x)return x;
	return f[x] = find(f[x]);
}

inline void unity(int x, int y)
{
	int u = find(x);
	int v = find(y);
	f[f[u]] = v;
}
//普通的dfs
void dfs(int u)
{
	int v = 9999;
	vis[u] = 1;
	for (int i = 0; i < G[u].size(); i++)
	{
		v = G[u][i];
		if (!ban[v] && !vis[v])
		{
			unity(u, v);
			dfs(v);
		}
	}
}
//核心代码↓
inline void work()
{
	asl.push_front(tpc);//最后的情况放链表首
	for (list<int>::iterator it = atk.begin(); it != atk.end(); it++)
	{
    	//欲添加的节点为u,u直接连接的节点为v
		int u = *it;
		S.clear();
		ban[u] = 0;
		for (int j = 0; j < G[u].size(); j++)
		{
			int v = G[u][j];
			if (ban[v])continue;
            //      ↑ v在之前被删除,不能选
			S.insert(find(v));
			unity(u, v);
		}
        
		if (S.size() == 0)tpc++;
		else if (S.size() == 1);
		else
		{
			tpc -= S.size();
			tpc++;
		}
		//tpc放asl首
		asl.push_front(tpc);
	}

}


int main()
{
	cin >> n >> m;
	int u = 0, v = 0;
	for (int i = 1; i <= m; i++)
	{
		u = read();
		v = read();
		G[u].push_back(v);
		G[v].push_back(u);
	}
	k = read();
	int t = 0;
	for (int i = 1; i <= k; i++)
	{
		t = read();
		ban[t] = 1;
		atk.push_front(t);
	}
	for (int i = 0; i < n; i++)f[i] = i;

	for (int i = 0; i < n; i++)
	{
		if (!vis[i] && !ban[i])
		{
			tpc++;
			dfs(i); 
		}
	}
	work();
	for (list<int>::iterator it = asl.begin(); it != asl.end(); it++)
		printf("%d\n", *it);

	return 0;
}

posted @ 2019-11-07 22:30  miyasaka  阅读(155)  评论(0)    收藏  举报