AT2675 [AGC018F] Two Trees (构造+二分图染色+并查集)

description

戳我看题目

solution

正解说是欧拉回路,但是于私而言非常难懂,如果有兴趣可以看香香mm的博客
定义一个点如果有偶数个儿子,就为奇点;如果有奇数个儿子,就为偶点
对于一个点的每个子树自身是满足 m o d   2 = 1 mod\ 2=1 mod 2=1

如果是偶点,那么奇数个儿子相加 m o d   2 mod\ 2 mod 2就已经满足要求了,点权就设为 0 0 0
如果是奇点,那么偶数个儿子相加取模就没了,点权应该设为 ± 1 ±1 ±1

单思考一个子树的情况,算上自己,整棵树内的奇点个数应为 2 k + 1 2k+1 2k+1
随便两两匹配后一定会孤出一个点来
这个点就通过 u u u点往上跟某一个孤的祖先匹配

两两匹配的点对就分别取 1 , − 1 1,-1 1,1,二分图染色可以搞

code

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 100005
int f[maxn], c[maxn], rnk[maxn];

int find( int u ) {
    
    
	if( u == f[u] ) return u;
	int fa = find( f[u] );
	c[u] ^= c[f[u]];
	return f[u] = fa;
}

void merge( int u, int v ) {
    
     //并查集按秩合并 
	int fu = find( u ), fv = find( v );
	if( fu == fv ) return;
	else if( rnk[fu] < rnk[fv] ) swap( fu, fv );
	else if( rnk[fu] == rnk[fv] ) rnk[fu] ++;
	c[fv] = c[u] ^ c[v] ^ 1;
	f[fv] = fu;
} 

struct node {
    
    
	vector < int > G[maxn];
	int siz[maxn];
	int root;
	
	void init( int n ) {
    
    
		for( int i = 1, fa;i <= n;i ++ ) {
    
    
			scanf( "%d", &fa );
			if( ~ fa ) G[fa].push_back( i ), siz[fa] ++;
			else root = i;
		}
	}
	
	int dfs( int u ) {
    
    
		vector < int > num;
		if( ! ( siz[u] & 1 ) ) num.push_back( u );//有偶数个儿子 即奇点 
		for( int i = 0;i < G[u].size();i ++ )
			num.push_back( dfs( G[u][i] ) );
		for( int i = 1;i < num.size();i += 2 ) //一定是2k+1个点 两两匹配 孤出一个点与上面祖先匹配
			merge( num[i], num[i + 1] );
		return num[0]; 
	}
	
}A, B;

int main() {
    
    
	int n;
	scanf( "%d", &n );
	A.init( n ), B.init( n );
	for( int i = 1;i <= n;i ++ )
		if( ( A.siz[i] + B.siz[i] ) & 1 ) //i点在两棵树上的奇偶性应一致 
			return ! printf( "IMPOSSIBLE\n" );
	printf( "POSSIBLE\n" );
	for( int i = 1;i <= n;i ++ ) f[i] = i;
	A.dfs( A.root ), B.dfs( B.root );
	for( int i = 1;i <= n;i ++ )
		if( ! ( A.siz[i] & 1 ) ) {
    
    
			find( i );
			if( c[i] ) printf( "1 " );
			else printf( "-1 " );
		}
		else printf( "0 " );
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Emm_Titan/article/details/113838511