P1330 封锁阳光大学

在这里存一下关于可爱的曹的题的做题笔记!

感觉细节想得开始有点混乱,所以写了很多注释来帮助顺思路w

(顺便吐槽一下这题下午基本打好了,浅调几个bug感觉就可以了,结果机房突然停电了qwq。甚至!连对面厕所都是有电的!!!)

(不过也当做给自己晚上重新顺一遍思路的机会算了(咬牙切齿))


思路。

曹实在是太可爱了怎么能阻止曹刷街呢qwq @兔绒绒@一只玉桂狗

咳咳,首先分析题干。可知两点比较重要的———

对于每一条边所连接的两点:1.至少要有一个被选中。 2.不能被同时选中。

呃呃呃我又想贪心了!感觉xsy有些大病qwq

显然,封锁只连了一条边的节点会亏大本,所以必须封锁这些边的另一个节点,即倒数第二层。

然后……似乎像这样向上推就可以了(?)

所以感觉可以并查集!

因为每隔一个节点就可以放河蟹了,所以模型就可以简化为黑白染色问题。

对于每个节点,如果把它封锁了那么就可以把这个节点的上一个节点与这个节点的下一个节点直接相连。

这样就可以全部串起来啦!最后简单标记相加即可~


代码。

#include <bits/stdc++.h>
//#define int long long
#define maxn 100005
#define maxm 10005
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int V,E;
int u,v,fu,fv;
int fa[maxn]; //父亲节点是上一个同色节点。
basic_string<int> edge[maxm]; 
int flag,head[maxn],ned[maxn];
bool vis[maxn];
int ans;

int fd(int x) { return fa[x]=(fa[x]==x)?x:fd(fa[x]);}

void check(int x,int fath)
{
	int fx=fd(x);
	if(fx!=fath) { fa[fath]=fx; ned[fx]+=ned[fath];} 
	//如果u上一个节点和v的父亲节点没有共同的父亲则可以合并这两个节点(连线+传need的河蟹数)
}

void init()
{
	cin>>V>>E;
	for(int i=1;i<=V;++i) fa[i]=i,ned[i]=1; //初始化。每个节点的fa先连自己,需要一个河蟹来封锁。
}

void doit()
{
	for(int i=1;i<=V;++i)
	{
		int fi=fd(i);
		if(!vis[fi])		
		{
			int fh=fd(head[i]); //i节点前一个节点的祖先。
			vis[fi]=vis[fh]=1;
			ans+=min(ned[fi],ned[fh]); 
			//可以选择封锁含i的家族节点,也可以选择封锁另一家族(即含head[i]的)
		}
	}
}

signed main()
{
	init(); 
	for(int i=1;i<=E;++i)
	{
		cin>>u>>v; 
		fu=fd(u),fv=fd(v);
		if(fu==fv){cout<<"Impossible";return 0;} //如果三个节点形成闭环则至少有一条边是无论如何无法封锁的。
		if(head[u]) check(head[u],fv); //head[u]表示u的上一个异色节点,而v一定与u异色,那么就可以判断得这两个节点是否同色。同色则合并。
		if(head[v]) check(head[v],fu);
		head[u]=fv,head[v]=fu; //更新head。
	}
	doit();
	cout<<ans;

	return 0;
}

大概就这样(?)

学完最近的图论专题可能会回来把图论的做题笔记都重温一下或者修正一些问题。
(如果不鸽的话x 咕咕咕咕咕咕咕咕咕咕)

posted @ 2022-05-15 21:43  筱星Shea  阅读(65)  评论(1编辑  收藏  举报