题解 recollection
传送门、
- 当题目是求所有点对之间操作的最值的时候记得随机点对+卡时
- 当题目是要求最值的时候记得随机决策点+卡时
首先放到广义SAM上,就变成了要求一棵树上 当前节点权值+当前节点子树内所有点对在另一棵树上lca的深度 的最大值
- 关于「当前节点子树内所有点对在另一棵树上lca的深度的最值」:
注意最值一定在子树内所有节点按dfs序排序后相邻的节点处取到
于是可以对每个节点启发式合并维护一个按在另一棵树上dfs序排序的set
每次插入一个元素的时候lower_bound得到排在这个点前/后面的点,求lca更新答案
复杂度 \(O(nlog^2n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
#define fir first
#define sec second
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int head[N], dep[N], fa[26][N], lg[N], mp[N<<1], back[N<<1], in[N], now, size, ans;
struct edge{int to, next, val;}e[N<<1];
inline void add(int s, int t, int val) {e[++size]={t, head[s], val}; head[s]=size;}
int lca(int a, int b) {
if (dep[a]<dep[b]) swap(a, b);
while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
if (a==b) return a;
for (int i=lg[dep[a]]-1; ~i; --i)
if (fa[i][a]!=fa[i][b])
a=fa[i][a], b=fa[i][b];
return fa[0][a];
}
struct GSAM{
int len[N<<1], fail[N<<1], tot;
queue<pair<int, int>> q;
map<int, int> tr[N<<1];
struct cmp{inline bool operator () (int a, int b) {return in[a]<in[b];}};
set<int, cmp> st[N<<1];
int head[N<<1], pos[N<<1], size;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
void init() {tot=0; memset(head, -1, sizeof(head)); fail[0]=-1;}
int nxt(int u, int v) {return (tr[u].find(v)==tr[u].end())?(tr[u][v]=++tot):tr[u][v];}
int insert(int c, int now) {
int cur=tr[now][c];
len[cur]=len[now]+1;
int p, q;
for (p=fail[now]; ~p&&tr[p].find(c)==tr[p].end(); tr[p][c]=cur,p=fail[p]);
if (p==-1) fail[cur]=0;
else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
else {
int cln=++tot;
len[cln]=len[p]+1;
fail[cln]=fail[q];
for (auto it:tr[q]) if (len[it.sec]) tr[cln][it.fir]=it.sec;
for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
fail[cur]=fail[q]=cln;
}
return cur;
}
void dfs(int u) {
st[u].insert(back[u]);
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
dfs(v);
if (st[v].size()>st[u].size()) swap(st[u], st[v]);
for (auto it:st[v]) {
auto t=st[u].lower_bound(it);
if (t!=st[u].end()) ans=max(ans, len[u]+dep[lca(*t, it)]-1); //, cout<<len[u]+dep[lca(*t, it)]-1<<endl;
if (t!=st[u].begin()) --t, ans=max(ans, len[u]+dep[lca(*t, it)]-1);
st[u].insert(it);
}
}
}
void build() {
for (auto it:tr[0]) q.push({it.fir, 0});
while (q.size()) {
pair<int, int> u=q.front(); q.pop();
int now=pos[tr[u.sec][u.fir]]=insert(u.fir, u.sec);
for (auto it:tr[now]) q.push({it.fir, now});
}
for (int i=1; i<=tot; ++i) add(fail[i], i);
for (int i=1; i<=n; ++i) back[pos[mp[i]]]=i;
dep[0]=1; dfs(0);
}
}s;
void dfs(int u, int p, int pa) {
mp[u]=p; in[u]=++now;
for (int i=1; i<25; ++i)
if (dep[u]>=1<<i) fa[i][u]=fa[i-1][fa[i-1][u]];
else break;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (v==pa) continue;
dep[v]=dep[u]+1;
fa[0][v]=u;
dfs(v, s.nxt(p, e[i].val), u);
}
}
signed main()
{
n=read();
memset(head, -1, sizeof(head));
for (int i=1; i<N; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for (int i=2,v,w; i<=n; ++i) {
v=read(); w=read();
add(i, v, w); add(v, i, w);
}
s.init();
dep[1]=1; dfs(1, 0, 0);
s.build();
printf("%d\n", ans);
return 0;
}