lampice
题目描述
Mirko 准备用 NN 个 LED 灯来装饰圣诞树。这 NN 个灯通过了 N-1N−1 根电线连接在一起,任意两个灯之间都能通过电线互相到达。并且我们知道所有灯的颜色。
装饰结束后,Mirko 发现了很多有趣的图案,其中他最感兴趣的是 palindromic segments。一个 palindromic segments 是一条灯 uu 和灯 vv 之间的路径,满足从 uu 到 vv 经过的灯的颜色序列和从从 vv 到 uu 经过的灯的颜色序列相同。
Mirko 想要知道最长的 palindromic segments 有多长。一个 palindromic segments 长度定义为这条路径上灯的个数。
输入格式
第一行包含一个整数 N,表示灯的个数。
第二行包含一个长度为 NN 的字符串,仅由小写字母组成,其中第 ii 个字符表示第 ii 个灯的颜色。
接下里 N-1N−1 行,每行包含两个整数 AA 和 BB (A \neq BA\=B),表示灯 AA 和 灯 BB 之间有一条电线直接相连。
输出格式
仅一行,包含一个整数表示最长的 palindromic segments 的长度。
输入输出样例
输入 #1
7
imanade
1 2
2 3
3 4
4 5
5 6
6 7
输出 #1
3
输入 #2
4
aabb
1 2
1 3
3 4
输出 #2
2
输入 #3
8
acdbabcd
1 6
6 7
6 3
3 4
4 5
5 2
8 5
输出 #3
5
前置芝士:点分治,二分,哈希
且不说我不会淀粉质,就算学了再写这道也炒鸡毒瘤QWQ
快读帮我狗过洛谷的时限
首先这个结果是存在单调性的(要分奇偶),如果存在
l
e
n
+
2
len +2
len+2的回文串,那么一定存在
l
e
n
len
len的回文串,所以我们可以二分回文串长度,(在我抄的代码中,先二分偶数长度的,再二分奇数长度的).
然后分治部分,计算对于一个"根" r o o t root root来说,是否有经过它的长度为 l e n len len的回文串,找到了就直接返回节约时间,没有就继续分治直到结束.(分治部分套模板函数就可以了)
比较难的是 c a l c ( 计 算 是 否 存 在 的 函 数 部 分 ) calc(计算是否存在的函数部分) calc(计算是否存在的函数部分)
它包含三个函数
d
f
s
1
,
d
f
s
,
d
f
s
2
dfs1,dfs,dfs2
dfs1,dfs,dfs2,下面会写细一点
M
A
:
计
算
子
树
中
最
深
的
深
度
,
防
止
越
界
h
s
1
:
从
高
位
往
低
位
−
>
从
子
节
点
往
根
方
向
的
哈
希
值
(
未
包
括
根
节
点
)
h
s
2
:
从
高
位
往
低
位
−
>
从
根
节
点
往
子
节
点
方
向
的
哈
希
值
(
包
括
根
节
点
)
m
a
:
哈
希
表
,
本
来
应
该
储
存
h
a
s
h
值
,
这
里
储
存
h
a
s
h
的
差
值
,
具
体
怎
么
存
看
代
码
MA:计算子树中最深的深度,防止越界 \\ hs1:从高位往低位 -> 从子节点往根方向的哈希值(未包括根节点)\\ hs2:从高位往低位 -> 从根节点往子节点方向的哈希值(包括根节点)\\ ma:哈希表,本来应该储存hash值,这里储存hash的差值,具体怎么存看代码\\
MA:计算子树中最深的深度,防止越界hs1:从高位往低位−>从子节点往根方向的哈希值(未包括根节点)hs2:从高位往低位−>从根节点往子节点方向的哈希值(包括根节点)ma:哈希表,本来应该储存hash值,这里储存hash的差值,具体怎么存看代码
阅读顺序最好是:dfs->dfs2->dfs1
inline void calc(int x,int len)
{
MA = 0;//统计子树中最深的,防止搜索越界
dfs1(x,0,0,1);
hs1[x] = 0;
for(int i = 0;i <= MA + 1;++ i) ma[i].clear();
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y]) continue;
dfs(y,x,1,len);
if(flag) return ;
dfs2(y,x,1,len);
if(flag == 1)
return ;
}
}
inline void dfs1(int x,int fa,int ha,int dep)
{
ha = 1ll * ha * P % mod + s[x];
ha %= mod;
MA = max(MA,dep);
hs2[x] = ha;
//sz[x] = 1;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y] || y == fa) continue;
dfs1(y,x,ha,dep + 1);
//sz[x] += sz[y];
}
}//这一部分就是求出hs2的值 + MA
inline void dfs(int x,int fa,int dep,int len)
{
hs1[x] = hs1[fa] + 1ll * p[dep - 1] * s[x] % mod;
hs1[x] %= mod;
//如果这个长度合适(当前不超过,且剩下的不超过最长深度)
if(dep < len && len - dep <= MA)
if(dep != len - 1 && ma[len - dep].count((1ll * p[len - dep] * hs1[x] % mod - hs2[x] + mod)% mod))//这里讨论的情况是根节点在回文串中间,并不在端点处.如果hash表中已经存在长度补齐且和当前差值相等的,说明有回文串
flag = 1;
if(flag) return;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y] || y == fa) continue;
dfs(y,x,dep + 1,len);
if(flag) return ;
}
}//这一部分是判断有无回文串并且求hs1
之前说存哈希的差值,仔细说一下:如果一个串是回文串(假设根是
R
R
R,两端是
a
,
b
a,b
a,b),那么从
a
−
>
R
−
>
b
=
b
−
>
R
−
>
a
a->R->b = b->R->a
a−>R−>b=b−>R−>a,也就是:
h
s
1
[
a
]
∗
p
[
l
e
n
a
]
+
h
s
2
[
b
]
=
h
s
1
[
b
]
∗
p
[
l
e
n
b
]
+
h
s
2
[
a
]
hs1[a] * p[lena] + hs2[b] = hs1[b] * p[lenb] + hs2[a]
hs1[a]∗p[lena]+hs2[b]=hs1[b]∗p[lenb]+hs2[a]
交换一下位置:
h
s
1
[
a
]
∗
p
[
l
e
n
a
]
−
h
s
2
[
a
]
=
h
s
1
[
b
]
∗
p
[
l
e
n
b
]
−
h
s
2
[
b
]
hs1[a] * p[lena] - hs2[a] = hs1[b] * p[lenb] - hs2[b]
hs1[a]∗p[lena]−hs2[a]=hs1[b]∗p[lenb]−hs2[b]
所以我们查找就是查这个,存也是存这个(区分一下hs1和hs2一些细节)
inline void dfs2(int x,int fa,int dep,int len)
{
if(dep < len - 1)
ma[dep + 1][(1ll * p[len - dep] * hs1[x] % mod - hs2[x] + mod) % mod] = 1;
if(dep == len - 1 && hs2[x] == (1ll * p[1] * hs1[x] % mod + s[root]) % mod)//此处特判根节点是某一端的情况,因为hs2包括了根节点,hs1不包括,所以处理一下
flag = 1;
if(flag) return ;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(y == fa || vis[y]) continue;
dfs2(y,x,dep + 1,len);
if(flag == 1) return ;
}
}
所以这三个函数一起大概就是这么一个思路:
1.先把子树中的hs2处理出来
2.对每一棵子树分别搜索得出hs1,比较之前加入的子树中的子串是否有相等的
3.将这一个子树的hs2 - hs1的值加入哈希表
4.重复2~3操作
完整代码:
#include<bits/stdc++.h>
#include <tr1/unordered_map>
using namespace std;
using namespace tr1;
const int N = 5e4 + 10;
const int mod = 1e9 + 7,P = 131;
int total,n,sz[N],maxson[N];
int hs1[N],hs2[N];
int root;
bool flag;
int MA;
int p[N];
int h[N],ne[N << 1],to[N << 1],cnt;
bool vis[N];
inline void add(int u,int v)
{
ne[++ cnt] = h[u];
to[cnt] = v;
h[u] = cnt;
}
char s[N];
tr1 :: unordered_map<int ,int >ma[N];
inline int read()//inline 加速读入
{
int x=0;char c=getchar();//x代表返回值,c代表读取的字符
while (c<'0'||c>'9') c=getchar();//读取所有非数部分
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();//如果读取的字符为数,加入返回值
return x;
}
inline void get_root(int fa,int x)
{
sz[x] = 1;
maxson[x] = 0;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(y == fa || vis[y]) continue;
get_root(x,y);
sz[x] += sz[y];
maxson[x] = max(maxson[x],sz[y]);
}
maxson[x] = max(maxson[x],total - sz[x]);
if(maxson[x] < maxson[root]) root = x;
}
inline void dfs1(int x,int fa,int ha,int dep)
{
ha = 1ll * ha * P % mod + s[x];
ha %= mod;
MA = max(MA,dep);
hs2[x] = ha;
sz[x] = 1;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y] || y == fa) continue;
dfs1(y,x,ha,dep + 1);
sz[x] += sz[y];
}
}
inline void dfs(int x,int fa,int dep,int len)
{
hs1[x] = hs1[fa] + 1ll * p[dep - 1] * s[x] % mod;
hs1[x] %= mod;
if(dep < len && len - dep <= MA)
if(dep != len - 1 && ma[len - dep].count((1ll * p[len - dep] * hs1[x] % mod - hs2[x] + mod)% mod))
flag = 1;
if(flag) return;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y] || y == fa) continue;
dfs(y,x,dep + 1,len);
if(flag) return ;
}
}
inline void dfs2(int x,int fa,int dep,int len)
{
if(dep < len - 1)
ma[dep + 1][(1ll * p[len - dep] * hs1[x] % mod - hs2[x] + mod) % mod] = 1;
if(dep == len - 1 && hs2[x] == (1ll * p[1] * hs1[x] % mod + s[root]) % mod)
flag = 1;
if(flag) return ;
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(y == fa || vis[y]) continue;
dfs2(y,x,dep + 1,len);
if(flag == 1) return ;
}
}
inline void calc(int x,int len)
{
MA = 0;
dfs1(x,0,0,1);
hs1[x] = 0;
for(int i = 0;i <= MA + 1;++ i) ma[i].clear();
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y]) continue;
dfs(y,x,1,len);
if(flag) return ;
dfs2(y,x,1,len);
if(flag == 1)
return ;
}
}
inline void solve(int x,int len)
{
vis[x] = 1;
calc(x,len);
for(int i = h[x];i;i = ne[i])
{
int y = to[i];
if(vis[y]) continue;
maxson[0] = total = sz[y];
root = 0;
get_root(x,y);
solve(root,len);
if(flag == 1) return ;
}
}
inline bool check(int x)
{
flag = 0;
root = 0;
total = maxson[0] = n;
get_root(0,1);
solve(root,x);
memset(vis,0,sizeof vis);
return flag;
}
int main()
{
// freopen("lampice.in","r",stdin);
scanf("%d",&n);
scanf("%s",s + 1);
p[0] = 1;
for(int i = 1;i <= n;++ i) p[i] = 1ll * p[i - 1] * P % mod;
for(int i = 1,u,v;i < n;++ i)
{
u = read();
v = read();
add(u,v);
add(v,u);
}
int l = 1, r = n / 2;
int ans = 1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid * 2))
l = mid + 1, ans = max(ans, mid * 2);
else
r = mid - 1;
}
l = 1, r = (n - 1) / 2;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid * 2 + 1))
l = mid + 1, ans = max(ans, mid * 2 + 1);
else
r = mid - 1;
}
cout << ans;
// cout << ans;
}
完结撒花