题解 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;
}
posted @ 2021-12-12 19:19  Administrator-09  阅读(1)  评论(0编辑  收藏  举报