ディレクトリ
@説明@
アリスとボブは、各ノードは、最初に黒または白のいずれかであり、nはノードのツリーでプレー。
アリス上側のハンドは、以下のステップを回し:
白色点v、経路(1、v)の全て染め黒を選択します。
最後に、人間のオペレータは、失うことができません。
勝つかどうかを計算し、ノードを選択可能なすべての第一歩を獲得するのに役立つアリス。
入力は、
最初の行は、n個のノードの数、1 <= N <= 100000を与えます 。
2行目は含まnは整数C1、C2、... CN、0 <= CI <= 1、 各ノードの説明の色。0は黒と白である1です。
次の行N-1、N-1は、ツリーエッジを説明し、各ラインは、二つの整数U、Vを含有します。
出力
、-1の出力を失った場合。
それ以外の場合は、昇順、すべての可能な最初のステップの選択で出力。
例えば
入力#1:
8
1 1 0 1 0 0 1 0
1 2
1 3
2 6
3 4
3 5
5 7
7 8
出力#1:
5
入力#2:
20
1 1 1 0 1 1 1 0 1 0 0 0 1 0 1 0 0 0 0 0
1 2
2 3
2 4
1 5
1 6
5 7
5 8
2 9
8 10
1 11
1 12
9 13
6 14
14 15
7 16
11 17
2 18
7 19
12 20
出力#2
8
11
12
@溶液@
パス削除を検討するための最初のステップ(1、v)は、実際には、いくつかのゲームの組み合わせです、いくつかの互いに素のサブ木(森)を得ました。
だから我々はこの問題を解決するために、SG再帰関数を使用することができます。
[X] SGを解く場合、単純なアプローチは、統計を削除する、サブ木Xツリーで白色点Iを列挙することである(X、I)森林SGのXORの残りの後、次いで、全てのために、このパスI MEXを求めて行われます。
この方法は、(N ^ 2)Oで発見、および最適化を検討することができます。
親へのxから考慮再帰が、実際にはすべてのxサブツリーチェーンの伸びが少しあり、SG Zhimoは、伸長した後、新たに生成するXOR。
すべての実行可能なXサブツリー、XORを維持し、全体的な統合をサポートするチェーンの構造を使用して、だけでなく、MEXを解決することを検討してください。
ヒューリスティック合併は確かに良いですが、このモデルは、複雑さのよりよいセグメントツリーの合併を達成するために使用することができます。
XOR MEXを解決しながら、しかし、連結セグメントツリーは非常に良い全体的なサポートではありません。しかし、この種のものはあなたが得るためにXOR 01トライを使用することができます。
マージするトライを用いた類似のセグメントツリーマージプロセスと考えることができ、総時間複雑度はO(n * LOGN)を行うことができます。
トライどのように排他的な再生したり、それをタグ付けするには?これは、異なる又は排他的タグ息子まで直接、または直接格納されている次のパスの数であってもよいです。i番目の層バイナリタグは、左右のサブツリーの交換である場合。
MEXを求めているとして、あなたはカバーがすべて存在する場合、間隔の数は、クラスツリーラインの半分が可能であることをことを示して保存することができます。
最後に、各時点でのDFSは勝利戦略で選択ゼロである値の最初の工程後の状況を算出することができます。
@acceptedコード@
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
struct edge{
edge *nxt; int to;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
struct node{
node *ch[2];
int tag; bool flag;
}pl[40*MAXN + 5], *rt[MAXN + 5], *ncnt, *NIL;
void init() {
ecnt = &edges[0], ncnt = NIL = &pl[0];
NIL->ch[0] = NIL->ch[1] = NIL;
}
node *new_trie(int k, int dep) {
node *p = (++ncnt); p->ch[0] = p->ch[1] = NIL; p->tag = 0;
if( dep == -1 ) {
p->flag = true;
return p;
}
if( (k >> dep) & 1 ) p->ch[1] = new_trie(k, dep - 1);
else p->ch[0] = new_trie(k, dep - 1);
return p;
}
void pushdown(node *rt, int dep) {
if( rt->tag ) {
if( (rt->tag >> dep) & 1 )
swap(rt->ch[0], rt->ch[1]);
if( rt->ch[0] != NIL ) rt->ch[0]->tag ^= rt->tag;
if( rt->ch[1] != NIL ) rt->ch[1]->tag ^= rt->tag;
rt->tag = 0;
}
}
void pushup(node *rt) {
if( rt->ch[0]->flag && rt->ch[1]->flag )
rt->flag = true;
}
node *merge(node *rt1, node *rt2, int dep) {
if( rt1 == NIL ) return rt2;
if( rt2 == NIL ) return rt1;
if( dep == -1 ) return rt1;
pushdown(rt1, dep), pushdown(rt2, dep);
rt1->ch[0] = merge(rt1->ch[0], rt2->ch[0], dep - 1);
rt1->ch[1] = merge(rt1->ch[1], rt2->ch[1], dep - 1);
pushup(rt1);
return rt1;
}
int search_mex(node *rt, int dep) {
if( dep == -1 ) return 0;
pushdown(rt, dep);
if( rt->ch[0]->flag )
return search_mex(rt->ch[1], dep - 1) | (1<<dep);
else return search_mex(rt->ch[0], dep - 1);
}
int c[MAXN + 5];
int sg[MAXN + 5], sum[MAXN + 5];
void dfs1(int x, int f) {
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs1(p->to, x); sum[x] ^= sg[p->to];
}
rt[x] = (c[x] ? NIL : new_trie(sum[x], 18));
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
rt[p->to]->tag ^= (sum[x] ^ sg[p->to]);
rt[x] = merge(rt[x], rt[p->to], 18);
}
sg[x] = search_mex(rt[x], 18);
//printf("! %d %d\n", x, sg[x]);
}
vector<int>ans;
void dfs2(int x, int f, int k) {
k ^= sum[x];
if( k == 0 && c[x] == 0 ) ans.push_back(x);
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs2(p->to, x, k^sg[p->to]);
}
}
int main() {
init();
int n; scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%d", &c[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs1(1, 0);
if( !sg[1] )
puts("-1");
else {
dfs2(1, 0, 0);
sort(ans.begin(), ans.end());
for(int i=0;i<ans.size();i++)
printf("%d\n", ans[i]);
}
}
@詳細@
あなたが合法的に白色点を選択する必要があることに注意してください。任意の時間(SG関数値を求めまたは法的第一歩シーク)黒点の寄与を考慮することができません。