CF global round18 D. X(or)-mas Tree

Problem - D - Codeforces

首先树上两点(x, y)间简单路径上边权异或和,可以转化成(root, x)上路径异或和 异或 (root, y)上路径异或和,因此我们可以把每个点点权定义为从根到当前点上边权异或和,同时发现两个数异或的奇偶性只跟两个数各自二进制下1个数奇偶性有关,可以用0/1态表示。

此时问题转化为:给定一棵树,一些树边已确定了连接的两点是相同态还是相反态,然后再给出一些形如树上两点点权是相同态还是相反态的限制,求可行方案。

显然是2-sat问题

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
//#define int long long
using namespace std;
const int N = 1e6 + 100;
const ll inf = 1e18;
const ll mod = 998244353;//1e9 + 7;

#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
	cerr << " " << to_string(H);
	debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

vector<pii> adj2[N];
VI adj[N];

bool vis[N];

int snum = 0;
int tim, dfn[N], low[N];
bool ins[N];
VI stk;

int ocr[N];

void dfs(int x)
{
	dfn[x] = low[x] = ++tim;
	vis[x] = 1;
	stk.pb(x);
	ins[x] = 1;
	trav(v, adj[x])
	{
		if(ins[v])
			low[x] = min(low[x], dfn[v]);
		if(!vis[v])
			dfs(v), low[x] = min(low[x], low[v]);
	}
	if(dfn[x] == low[x])
	{
		++snum;
		while(1)
		{
			int nw = stk.back();
			stk.pop_back();
			ins[nw] = 0;
			ocr[nw] = snum;
			if(nw == x)
				break;
		}
	}
}

int ans[N], res[N];

void dfs2(int x, int ff)
{
	trav(v, adj2[x])
	{
		if(v.fi == ff)
			continue;
		if(v.sc != -1)
			res[v.fi] = res[x] ^ v.sc, dfs2(v.fi, x);
		else
		{
			if(ans[x] == ans[v.fi])
				res[v.fi] = res[x];
			else
				res[v.fi] = res[x] ^ 1;
			dfs2(v.fi, x);
		}
	}
}

pii edge[N];

void sol()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n + n; i++)
		adj[i].clear(), adj2[i].clear(), vis[i] = 0;
	for(int i = 1; i < n; i++)
	{
		int x, y, z;
		cin >> x >> y >> z;
		adj2[x].pb(pii(y, z));
		adj2[y].pb(pii(x, z));
		edge[i] = pii(x, y);
		if(z != -1)
		{
			int num = __builtin_popcount(z);
			if(num & 1)
			{
				adj[x].pb(y + n);
				adj[y + n].pb(x);
				adj[x + n].pb(y);
				adj[y].pb(x + n);
			}
			else
			{
				adj[x].pb(y);
				adj[x + n].pb(y + n);
				adj[y].pb(x);
				adj[y + n].pb(x + n);
			}
		}
	}
	for(int i = 1; i <= m; i++)
	{
		int x, y, z;
		cin >> x >> y >> z;
		int num = __builtin_popcount(z);
		if(num & 1)
		{
			adj[x].pb(y + n);
			adj[y + n].pb(x);
			adj[x + n].pb(y);
			adj[y].pb(x + n);
		}
		else
		{
			adj[x].pb(y);
			adj[x + n].pb(y + n);
			adj[y].pb(x);
			adj[y + n].pb(x + n);
		}
	}
	for(int i = 1; i <= n + n; i++)
	{
		if(!vis[i])
			dfs(i);
	}
	for(int i = 1; i <= n; i++)
	{
		int x = i, y = i + n;
		if(ocr[x] == ocr[y])
		{
			cout << "NO" << '\n';
			return;
		}
		if(ocr[x] < ocr[y])
			ans[i] = 0;
		else 
			ans[i] = 1;
	}
	cout << "YES" << '\n';
	res[1] = ans[1], dfs2(1, 0);
	for(int i = 1; i < n; i++)
		cout << edge[i].fi << ' ' <<edge[i].sc << ' ' << (res[edge[i].fi] ^ res[edge[i].sc]) << "\n";
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int tt;
	cin >> tt;
	while(tt--)
		sol();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值