人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

题解:P9923 [POI 2023/2024 R1] Przyciski

前言

不错的二合一题。

思路分析

首先套路地建出二分图:对于 (x,y) 的点,连 xy+n 的无向边,这样问题转化为选出若干条边,使得所有点的度的奇偶性相同。

发现奇数解和偶数解具有不同的性质,考虑分别处理。

偶数解

图上有环即可。

直接 DFS 找环判断。

奇数解

在没有偶数解的前提下判断。

如果图上没有环,那么图一定是一个森林。

考虑一种经典的处理方式:从叶子往上选边。

具体地,我们从叶子开始选择边,因为叶子只和它的父亲相连,所以这些连接叶子的边一定要选,然后可以把叶子删去,这样有一些父亲就成了新的叶子,继续处理就行。

直接 DFS 处理就行。

如果一个点是根节点,并且它需要和父亲选边,那么一定无解。

把两种情况拼一起就做完了。

注意找环时的细节。

总体复杂度 O(n)

代码实现

写的比较丑。

#include<bits/stdc++.h>
using namespace std;
int n,m,u[500005],v[500005];
int head[200005],nxt[1000005],targetx[1000005],targetw[1000005],tot=1;
void add(int x,int y,int w){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	targetx[tot]=y;
	targetw[tot]=w;
}
bool vis1[200005],vis2[200005],vis3[1000005],vis4[200005],flag;
stack<int> s;
inline void dfs1(int x,int edge){
	if(flag) return;
	vis1[x]=vis2[x]=1;
	s.push(x);
	for(int i=head[x];i;i=nxt[i]){
		int y=targetx[i],w=targetw[i]; 
		if(vis3[w]) continue;
		if(vis2[y]){
			flag=true;
			while(s.top()!=y){
				vis4[s.top()]=true;
				s.pop();
			}
			vis4[y]=1;
			vis3[w]=1;
			if(flag){
				cout<<"TAK"<<'\n';
				int cnt=0;
				for(int j=1;j<=m;j++){
					if(vis3[j] && vis4[u[j]] && vis4[v[j]+n]){
						cnt++;
					}
				}
				cout<<cnt<<'\n';
				for(int j=1;j<=m;j++){
					if(vis3[j] && vis4[u[j]] && vis4[v[j]+n]){
						cout<<j<<' ';
					}
				}
				exit(0);
		    }
		}
		if(vis1[y]) continue;
		vis3[w]=1;
		dfs1(y,w);
		vis3[w]=0;
	}
	vis2[x]=0;
	s.pop();
}
int val[200005];
inline void dfs2(int x,int fa,int edge){
	vis1[x]=1;
	for(int i=head[x];i;i=nxt[i]){
		int y=targetx[i],w=targetw[i];
		if(y==fa) continue;
		dfs2(y,x,w); 
	}
	if(!val[x] && !fa){
		cout<<"NIE";
		exit(0);
	}
	if(!val[x]){
		val[x]^=1;
		val[fa]^=1;
		vis3[edge]=1;
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>u[i]>>v[i];
		add(u[i],v[i]+n,i);
		add(v[i]+n,u[i],i); 
	}
	for(int i=1;i<=n+n;i++){
		if(!vis1[i]){
			dfs1(i,0);
		}
	}
	memset(vis3,0,sizeof(vis3));
	memset(vis1,0,sizeof(vis1));
	for(int i=1;i<=n;i++){
		if(!vis1[i]){
			dfs2(i,0,0);
		} 
	}
	cout<<"TAK"<<'\n';
	int cnt=0;
	for(int i=1;i<=m;i++){
		if(vis3[i]){
			cnt++;
		}
	}
	cout<<cnt<<'\n';
	for(int i=1;i<=m;i++){
		if(vis3[i]){
		    cout<<i<<' ';
		}
	}
	return 0;
}

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18689769

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起