题解 P4323 【[JSOI2016]独特的树叶】
zhiyangfan · · 题解
前言
这题调了我两天,最终在机房大佬的指导下A了。 泪目,发题解庆祝一下。
题意
给出两棵树
思路
考虑树哈希(蒻蒟不会直径之类的判同构qwq)。
这里采用
注意一点注意一点!
哈希的一个重要性质就是对于
然后就是转移问题,考虑二次扫描换根树形dp。
插入时处理一个
然后先以 1 为根扫一遍 dfs,记录下每个节点的父节点
对于每一个节点
那么怎么维护
注意到
emm如果没看懂,就,结合代码,再画画图,应该,就行,了。
查询就简单了,
注意
(PS 这种前后缀合并的思想在换根dp中蛮常用,记到就是赚到)
关于代码
因为调了蛮久,所以可能有点丑陋。而且其实可以对于
最后注意哈希值的选择
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using std::vector;
using std::sort;
using std::lower_bound;
using std::set;
typedef long long ll;
const int N = 1e5 + 10, base = 1000000021, mod = 999999137;
struct edge
{
int v, next;
}A[N * 2], B[N * 2];
int a[N], b[N], ca, cb, n, ans = 99999999, faa[N], fab[N], deg[N];
void init()
{
memset(a, -1, sizeof a);
memset(b, -1, sizeof b);
ca = cb = 0;
}
void insertA(int u, int v)
{
A[ca].v = v;
A[ca].next = a[u];
a[u] = ca++;
}
void insertB(int u, int v)
{
B[cb].v = v;
deg[u]++;
B[cb].next = b[u];
b[u] = cb++;
}
ll hashA[N], hashB[N], sza[N], szb[N];
vector<ll> ta[N], sla[N], sra[N], tb[N], slb[N], srb[N];
ll dfsA1(int u, int fa)
{
ta[u].clear(), faa[u] = fa, sza[u] = 1;
ll ret = 0;
for (int i = a[u]; i + 1; i = A[i].next)
{
int v = A[i].v;
if (v == fa)
continue;
ta[u].push_back(dfsA1(v, u));
sza[u] += sza[v];
}
if (ta[u].empty())
return hashA[u] = 1;
sort(ta[u].begin(), ta[u].end());
for (int i = 0; i < (int)ta[u].size(); i++)
ret = (ret * base + ta[u][i]) % mod;
return hashA[u] = (ret * sza[u]) % mod;
}
ll dfsB1(int u, int fa)
{
tb[u].clear(), fab[u] = fa, szb[u] = 1;
ll ret = 0;
for (int i = b[u]; i + 1; i = B[i].next)
{
int v = B[i].v;
if (v == fa)
continue;
tb[u].push_back(dfsB1(v, u));
szb[u] += szb[v];
}
if (tb[u].empty())
return hashB[u] = 1;
sort(tb[u].begin(), tb[u].end());
for (int i = 0; i < (int)tb[u].size(); i++)
ret = (ret * base + tb[u][i]) % mod;
return hashB[u] = (ret * szb[u]) % mod;
}
ll gA[N], gB[N], pb[N];
set<ll> vis;
void dfsA2(int u)
{
if (faa[u] != -1)
{
ta[u].push_back(gA[u]);
sort(ta[u].begin(), ta[u].end());
}
int sze = ta[u].size();
sla[u].resize(sze);
sla[u][0] = ta[u][0];
for (int i = 1; i < sze; i++)
sla[u][i] = (sla[u][i - 1] * base + ta[u][i]) % mod;
sra[u].resize(sze);
sra[u][sze - 1] = ta[u][sze - 1];
for (int i = sze - 2; i >= 0; i--)
sra[u][i] = (sra[u][i + 1] + ta[u][i] * pb[sze - i - 1]) % mod;
for (int i = a[u]; i + 1; i = A[i].next)
{
int v = A[i].v;
if (v == faa[u])
continue;
if (sze == 1)
{
gA[v] = 1;
dfsA2(v);
break;
}
int p = lower_bound(ta[u].begin(), ta[u].end(), hashA[v]) - ta[u].begin();
gA[v] = 0;
if (p + 1 < sze)
gA[v] = sra[u][p + 1];
if (p - 1 >= 0)
gA[v] = (gA[v] + sla[u][p - 1]
* pb[sze - 1 - p]) % mod;
gA[v] = gA[v] * (n - sza[v]) % mod;
dfsA2(v);
}
vis.insert(sla[u][sze - 1] * n % mod);
}
void dfsB2(int u)
{
if (fab[u] != -1)
{
tb[u].push_back(gB[u]);
sort(tb[u].begin(), tb[u].end());
}
int sze = tb[u].size();
slb[u].resize(sze);
slb[u][0] = tb[u][0];
for (int i = 1; i < sze; i++)
slb[u][i] = (slb[u][i - 1] * base + tb[u][i]) % mod;
srb[u].resize(sze);
srb[u][sze - 1] = tb[u][sze - 1];
for (int i = sze - 2; i >= 0; i--)
srb[u][i] = (srb[u][i + 1] + tb[u][i] * pb[sze - i - 1]) % mod;
for (int i = b[u]; i + 1; i = B[i].next)
{
int v = B[i].v;
if (v == fab[u])
continue;
if (sze == 1)
{
gB[v] = 1;
dfsB2(v);
break;
}
int p = lower_bound(tb[u].begin(), tb[u].end(), hashB[v]) - tb[u].begin();
gB[v] = 0;
if (p + 1 < sze)
gB[v] = srb[u][p + 1];
if (p - 1 >= 0)
gB[v] = (gB[v] + slb[u][p - 1] * pb[sze - 1 - p]) % mod;
gB[v] = gB[v] * (n - szb[v]) % mod;
if (deg[v] == 1 && vis.find(gB[v]) != vis.end())
ans = std::min(ans, v);
dfsB2(v);
}
}
int main()
{
init();
pb[0] = 1;
for (int i = 1; i < N; i++)
pb[i] = pb[i - 1] * base % mod;
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
insertA(x, y);
insertA(y, x);
}
for (int i = 1; i <= n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
insertB(x, y);
insertB(y, x);
}
dfsA1(1, -1);
dfsA2(1);
n++;
dfsB1(1, -1);
if(deg[1] == 1 && vis.find(hashB[B[b[1]].v]) != vis.end())
ans = 1;
dfsB2(1);
printf("%d", ans);
return 0;
}