Problem:
Score : 1400 points

Problem Statement

There is a tree with N vertices numbered 1 through N.
The i-th of the N-1 edges connects vertices a_i and b_i.

Initially, each edge is painted blue.
Takahashi will convert this blue tree into a red tree, by performing the following operation N-1 times:

Select a simple path that consists of only blue edges, and remove one of those edges.

Then, span a new red edge between the two endpoints of the selected path.

His objective is to obtain a tree that has a red edge connecting vertices c_i and d_i, for each i.

Determine whether this is achievable.

Constraints

2 ≤ N ≤ 10^5

1 ≤ a_i,b_i,c_i,d_i ≤ N

a_i ≠ b_i

c_i ≠ d_i

Both input graphs are trees.

Input

Input is given from Standard Input in the following format:

N
a_1 b_1
:
a_{N-1} b_{N-1}
c_1 d_1
:
c_{N-1} d_{N-1}

Output

Print YES if the objective is achievable; print NO otherwise.

Sample Input 1

3
1 2
2 3
1 3
3 2

Sample Output 1

YES

The objective is achievable, as below:

First, select the path connecting vertices 1 and 3, and remove a blue edge 1-2. Then, span a new red edge 1-3.

Next, select the path connecting vertices 2 and 3, and remove a blue edge 2-3. Then, span a new red edge 2-3.

Sample Input 2

5
1 2
2 3
3 4
4 5
3 4
2 4
1 4
1 5

Sample Output 2

YES

Sample Input 3

6
1 2
3 5
4 6
1 6
5 1
5 3
1 4
2 6
4 3
5 6

Sample Output 3

NO

Solution in C++14 (GCC 5.4.1) (judge verdict: Memory Limit Exceeded)

#include <bits/stdc++.h>
#define endl '\n'

using namespace std;
const int MAXN = (1 << 20);
const int MAXLOG = 24;
const int inf = (int)1e9;

struct node2
{
	pair<int, int> mx;

	node2() {mx = {-1e9, -1e9};}
	node2(pair<int, int> val)
	{
		mx = val;
	}
};

node2 temp2, broken2;

node2 merge(node2 l, node2 r)
{
	temp2.mx = max(l.mx, r.mx);
	return temp2;
}

struct segment_tree2
{
    multiset<pair<int, int> > st[4 * MAXN];
	node2 tr[4 * MAXN];

	void init(int l, int r, int idx)
	{
		if(l == r)
		{
			tr[idx] = node2();
			return;
		}

		int mid = (l + r) >> 1;
		init(l, mid, 2 * idx + 1);
		init(mid + 1, r, 2 * idx + 2);

		tr[idx] = merge(tr[2 * idx + 1], tr[2 * idx + 2]);
	}

	void erase(int pos, pair<int, int> val, int l, int r, int idx)
	{
		if(l > pos || r < pos)
			return;

		if(l == r && l == pos)
		{
            st[idx].erase(st[idx].find(val));
			if(st[idx].empty()) tr[idx].mx = {-1e9, -1e9};
			else tr[idx].mx = *prev(st[idx].end());
			return;
		}

		int mid = (l + r) >> 1;
		erase(pos, val, l, mid, 2 * idx + 1);
		erase(pos, val, mid + 1, r, 2 * idx + 2);

		tr[idx] = merge(tr[2 * idx + 1], tr[2 * idx + 2]);
	}


	void update(int pos, pair<int, int> val, int l, int r, int idx)
	{
		if(l > pos || r < pos)
			return;

		if(l == r && l == pos)
		{
            st[idx].insert(val);
			if(st[idx].empty()) tr[idx].mx = {-1e9, -1e9};
			else tr[idx].mx = *prev(st[idx].end());
			return;
		}

		int mid = (l + r) >> 1;
		update(pos, val, l, mid, 2 * idx + 1);
		update(pos, val, mid + 1, r, 2 * idx + 2);

		tr[idx] = merge(tr[2 * idx + 1], tr[2 * idx + 2]);
	}

	node2 query(int qL, int qR, int l, int r, int idx)
	{
		if(l > qR || r < qL)
			return broken2;

		if(qL <= l && r <= qR)
			return tr[idx];

		int mid = (l + r) >> 1;
		return merge(query(qL, qR, l, mid, 2 * idx + 1), query(qL, qR, mid + 1, r, 2 * idx + 2));
	}
};

int st[MAXN], en[MAXN], de[MAXN];
int par[MAXN][MAXLOG], lgn, dfs_time;
int tr_sz[MAXN], head[MAXN], root, vert[MAXN];

struct node
{
	pair<int, int> mx, mn;
	int lazy;

	node() { mx = {inf, -1}; mn = {inf, -1}; lazy = 0; }
	node(int val, int pos)
	{
		mx = {val, pos};
		mn = {val, pos};
		lazy = 0;
	}
};

node temp, broken;

node merge(node l, node r)
{
	temp.mx = min(l.mx, r.mx);
	temp.mn = min(l.mn, r.mn);
	temp.lazy = 0;
	return temp;
}

struct segment_tree
{
	node tr[4 * MAXN];

	void push(int l, int r, int idx)
	{
		if(tr[idx].lazy)
		{
			tr[idx].mx.first += tr[idx].lazy;
			tr[idx].mn.first += tr[idx].lazy;

			if(l != r)
			{
				tr[2 * idx + 1].lazy += tr[idx].lazy;
				tr[2 * idx + 2].lazy += tr[idx].lazy;
			}

			tr[idx].lazy = 0;
		}
	}

	void init(int l, int r, int idx)
	{
		if(l == r)
		{
			tr[idx] = node(0, l);
			return;
		}

		int mid = (l + r) >> 1;
		init(l, mid, 2 * idx + 1);
		init(mid + 1, r, 2 * idx + 2);

		tr[idx] = merge(tr[2 * idx + 1], tr[2 * idx + 2]);
	}

	void add(int qL, int qR, int val, int l, int r, int idx)
	{
		push(l, r, idx);

		if(qL > r || l > qR)
			return;

		if(qL <= l && r <= qR)
		{
			tr[idx].lazy += val;
			push(l, r, idx);
			return;
		}

		int mid = (l + r) >> 1;
		add(qL, qR, val, l, mid, 2 * idx + 1);
		add(qL, qR, val, mid + 1, r, 2 * idx + 2);

		tr[idx] = merge(tr[2 * idx + 1], tr[2 * idx + 2]);
	}

	void del(int pos, int l, int r, int idx)
	{
		push(l, r, idx);

		if(pos > r || l > pos)
			return;

		if(l == pos && r == pos)
		{
            tr[idx].mn = {inf, -1};
			return;
		}

		int mid = (l + r) >> 1;
		del(pos, l, mid, 2 * idx + 1);
		del(pos, mid + 1, r, 2 * idx + 2);

		tr[idx] = merge(tr[2 * idx + 1], tr[2 * idx + 2]);
	}

	node query(int qL, int qR, int l, int r, int idx)
	{
		push(l, r, idx);

		if(l > qR || r < qL)
			return broken;

		if(qL <= l && r <= qR)
			return tr[idx];

		int mid = (l + r) >> 1;
		return merge(query(qL, qR, l, mid, 2 * idx + 1), query(qL, qR, mid + 1, r, 2 * idx + 2));
	}
};

int n;
vector<int> G[MAXN];

void read()
{
	cin >> n;

	for(int i = 0; i < n - 1; i++)
	{
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
}

segment_tree t;

void pre_dfs(int u, int pr)
{
	tr_sz[u] = 1;
	for(int v: G[u])
		if(v != pr)
		{
			pre_dfs(v, u);
			tr_sz[u] += tr_sz[v];
		}
}

void decompose(int u, int head, int pr, int dep = 0)
{
	par[u][0] = pr;
	for(int l = 1; l <= lgn; l++)
		par[u][l] = par[par[u][l - 1]][l - 1];

    de[u] = dep;
	st[u] = ++dfs_time;
	vert[dfs_time] = u;
	::head[u] = head;

	pair<int, int> mx = {-1, -1};
	for(int v: G[u])
		if(v != pr)
			mx = max(mx, {tr_sz[v], v});

	if(mx.second != -1)
		decompose(mx.second, head, u, dep + 1);

	for(int v: G[u])
		if(v != pr && v != mx.second)
			decompose(v, v, u, dep + 1);

	en[u] = dfs_time;
}

inline bool upper(int u, int v) { return st[u] <= st[v] && en[v] <= en[u]; }

int lca(int u, int v)
{
	if(upper(u, v)) return u;
	if(upper(v, u)) return v;

	for(int l = lgn; l >= 0; l--)
		if(!upper(par[u][l], v))
			u = par[u][l];

	return par[u][0];
}

void hld_precompute(int _root)
{
	root = _root;
	lgn = 0; while((1 << lgn) < n) lgn++;
	dfs_time = 0;

	pre_dfs(root, root);
	decompose(root, root, root);
}

node get_path(int u, int anc)
{
	node ret = broken;
	while(st[anc] < st[u])
	{
		ret = merge(ret, t.query(max(st[anc] + 1, st[head[u]]), st[u], 1, dfs_time, 0));
		u = par[head[u]][0];
	}

	return ret;
}

void add_path(int u, int anc, int val)
{
	while(st[anc] < st[u])
	{
		t.add(max(st[anc] + 1, st[head[u]]), st[u], val, 1, dfs_time, 0);
		u = par[head[u]][0];
	}
}

node query(int u, int v)
{
    int anc = lca(u, v);
    node ret = merge(get_path(u, anc), get_path(v, anc));
    return ret;
}

void add(int u, int v, int val)
{
    int anc = lca(u, v);
    add_path(u, anc, val);
    add_path(v, anc, val);
}

int dist(int u, int v) { return de[u] + de[v] - 2 * de[lca(u, v)]; }

segment_tree2 t2;

void gen_path(int u, int anc, int idx)
{
	while(st[anc] < st[u])
	{
		t2.update(max(st[head[u]], st[anc] + 1), {st[u], idx}, 1, dfs_time, 0);
		u = par[head[u]][0];
	}
}

void rem_path(int u, int anc, int idx)
{
	while(st[anc] < st[u])
	{
		t2.erase(max(st[head[u]], st[anc] + 1), {st[u], idx}, 1, dfs_time, 0);
		u = par[head[u]][0];
	}
}

void gen(int u, int v, int val)
{
    int anc = lca(u, v);
    gen_path(u, anc, val);
    gen_path(v, anc, val);
}

void rem(int u, int v, int val)
{
    int anc = lca(u, v);
    rem_path(u, anc, val);
    rem_path(v, anc, val);
}

void solve()
{
	hld_precompute(1);
	t.init(1, dfs_time, 0);
	t2.init(1, dfs_time, 0);

	vector<pair<int, int> > e;

    for(int i = 0; i < n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        add(u, v, 1);
        gen(u, v, i);
        e.push_back({u, v});
    }

    for(int ii = 0; ii < n - 1; ii++)
    {
        pair<int, int> mx = t.query(2, dfs_time, 1, dfs_time, 0).mn;

        if(mx.first != 1)
        {
            cout << "NO" << endl;
            return;
        }

        int i = t2.query(2, mx.second, 1, dfs_time, 0).mx.second, u = e[i].first, v = e[i].second;
        mx = query(u, v).mx;
        if(mx.first != 1)
        {
            cout << "NO" << endl;
            return;
        }

        add(u, v, -1);
        t.del(mx.second, 1, dfs_time, 0);
        rem(u, v, i);
    }

    cout << "YES" << endl;
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	read();
	solve();
	return 0;
}