【Spoj COT3】SG函数 Trie启发式合并

3 篇文章 0 订阅
2 篇文章 0 订阅

给一颗有黑白点的有根树,每次可以选一个白点并将其到根的路径染黑,问先手是否必胜以及第1步的可能选择。

子树间相互独立,用Trie维护一个子树操作一次可达SG值,这个用Trie启发式合并可求,需要打标记。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
#define u t[x]
#define o t[y]
#define Lc t[u.lc]
#define Rc t[u.rc]
using namespace std;
typedef long long LL;
const int N = 100005;
struct Edge { int y, nex; } g[N * 2];
struct arr { int lc, rc, lz; bool f; } t[N * 70];
int n, pos[N], sz, tsz, ans[N], az, q, f[N], ro[N], c[N];
void Init(int x, int y) { g[++ sz] = (Edge) { y, pos[x] }, pos[x] = sz; }
void PD(int x, int dt) {
	if (!x || !u.lz) return ;
	if (u.lz & (1 << dt-1)) swap(u.lc, u.rc);
	Lc.lz ^= u.lz, Rc.lz ^= u.lz; u.lz = 0;
}
void Merge(int &x, int y, int dt) {
	if (!x || !y) { x += y; return ; }
	if (!dt) return ;
	PD(x, dt), PD(y, dt);
	Merge(u.lc, o.lc, dt - 1), Merge(u.rc, o.rc, dt - 1);
	u.f = Lc.f & Rc.f;
}
int Find(int x, int dt) {
	if (!dt) return 0;
	PD(x, dt);
	if (Lc.f) return Find(u.rc, dt - 1) + (1 << dt-1);
	else return Find(u.lc, dt - 1);
}
void Add(int &x, int dt) {
	if (!x) x = ++ tsz;
	if (!dt) { u.f = 1; return ; }
	PD(x, dt);
	if (q & (1 << dt-1)) Add(u.rc, dt - 1); else Add(u.lc, dt - 1);
	u.f = Lc.f & Rc.f;
}
void Dfs(int x, int ft) {
	int z = 0;
	RepE(i, x) {
		int y = g[i].y; if (y == ft) continue ;
		Dfs(y, x), z ^= f[y];
	}
	RepE(i, x) {
		int y = g[i].y; if (y == ft) continue ;
		t[ ro[y] ].lz ^= (z ^ f[y]); Merge(ro[x], ro[y], 17);
	}
	if (c[x]) q = z, Add(ro[x], 17); f[x] = Find(ro[x], 17);
}
void Print(int x, int ft, int z) {
	int r = 0;
	RepE(i, x) if (g[i].y != ft) r ^= f[ g[i].y ];
	RepE(i, x) {
		int y = g[i].y; if (y == ft) continue ;
		Print(y, x, z ^ r ^ f[y]);
	}
	if (c[x] && (z ^ r) == 0) ans[++ az] = x;
}
int main()
{
	scanf ("%d", &n);
	Rep(i, 1, n) scanf ("%d", &c[i]), c[i] ^= 1;
	Rep(i, 1, n - 1) {
		int x, y;
		scanf ("%d%d", &x, &y), Init(x, y), Init(y, x);
	}
	Dfs(1, 1);
	if (!f[1]) { puts("-1"); return 0; }
	Print(1, 1, 0);
	sort(ans+1, ans+az+1);
	Rep(i, 1, az) printf("%d\n", ans[i]);

	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值