F - Two Trees
题意:
给定两棵都是N个节点的有根树,节点均从1~N标号。给每个标号定一个权值(类似一号点的权值是x,那么两棵树中1号点的权值都是x),使在两棵树满足以任意节点为根的子树的权值和为1或-1。输出任意一种解或判断无解,N<=100000。
分析:
欧拉回路。
首先每棵子树的权值和都-1或者1,,如果知道了每个点有多少个子节点,那么就可以知道他的奇偶性(奇数个儿子=>权值为偶数,偶数个儿子=>权值为奇数)。现在可以判断无解了:如果同一个节点在两棵树内的奇偶性不同。
然后建立一个0点,将两棵树连起来;然后一个标号的点在两棵树上是奇点,那么有第一棵树的这个点向第二棵树的这个点连一条边(x->x+n)。然后跑欧拉回路,对于从第一棵树到第二颗树的点,权值为1,否则为-1,偶点为0。
具体的解释证明,画一下,挺对的。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int N = 500005; struct Edge{ int fr, to, nxt; }e[N << 1]; int head[N], f1[N], f2[N], c1[N], c2[N], sk[N << 1], ans[N], top = 0, En = 1; bool vis[N << 1]; inline void add_edge(int u,int v) { // cout << u << " " << v << " "; ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u]; head[u] = En; ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v]; head[v] = En; } void dfs(int u) { while (1) { int k = head[u]; if (!k) break; head[u] = e[k].nxt; if (!vis[k]) { vis[k] = vis[k ^ 1] = 1; dfs(e[k].to); sk[++top] = k; } } } int main() { freopen("1.txt", "r", stdin); int n = read(); for (int i = 1; i <= n; ++i) { int x = read(); if (x == -1) x = 0; f1[i] = x, c1[x] ++; add_edge(x, i); } for (int i = 1; i <= n; ++i) { int x = read(); if (x == -1) x = 0; f2[i] = x, c2[x] ++; if (x == 0) add_edge(x, i + n); else add_edge(x + n, i + n); } for (int i = 1; i <= n; ++i) { if (c1[i] % 2 != c2[i] % 2) { puts("IMPOSSIBLE"); return 0; } if ((c1[i] & 1) && (c2[i] & 1)) ans[i] = 0; else add_edge(i, i + n); } puts("POSSIBLE"); dfs(0); for (int i = top; i >= 1; --i) { int k = sk[i]; if (e[k].fr + n == e[k].to) ans[e[k].fr] = 1; if (e[k].fr - n == e[k].to) ans[e[k].fr - n] = -1; } for (int i = 1; i <= n; ++i) { printf("%d ", ans[i]); } return 0; }