题解 万灵药

传送门

发现可能的 DNA 序列总是两个前缀的 lcs
放到 SAM 上就是两个节点在 fail 树上的 lca
发现这样的 lca 只有 \(O(n)\) 个,暴力插到一棵 trie 树里
然后加 DNA 序列就是对每个 lca 统计贡献,可以暴跳父亲树剖维护

然后发现这棵 trie 的大小貌似是 \(O(n)\) 级别的
然后又发现因为插进去的都是 SAM 上的节点
而每个 SAM 节点在 DAG 上又都存在一个 \(len=len_i-1\) 的前驱
那么直接找到所有这样的边就是 trie 树上的边,就可以 \(O(n)\) 建树了
然后还是树剖维护,因为正解是全局平衡二叉树所以在某个 OJ不太能卡过
复杂度 \(O(n\log^2 n)\)

点击查看代码
// ubsan: undefined
// accoders
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC target("avx", "sse")
#pragma GCC optimize("inline")
#pragma GCC optimize("unroll-loops")

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define pb push_back
#define ll long long
//#define int long long

int n, q;
ll ans;
char s[N];
bool vis[N];
vector<int> to[N];
int msiz[N], mson[N], top[N], id[N], rk[N], bit[N];
int len[N], fail[N], tr[N][4], back[N], lg[N], pos[N], siz[N], dep[N], now, tot, all;
void init() {fail[now=tot=0]=-1;}
inline void add(int i, int dat) {for (; i<=all; i+=i&-i) bit[i]+=dat;}
inline int query(int l, int r) {
	int ans=0; --l;
	while (r>l) ans+=bit[r], r-=r&-r;
	while (l>r) ans-=bit[l], l-=l&-l;
	return ans;
}
void insert(char c) {
	c-='a';
	int cur=++tot;
	len[cur]=len[now]+1;
	int p, q;
	for (p=now; ~p&&!tr[p][c]; 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 (int i=0; i<4; ++i) tr[cln][i]=tr[q][i];
		for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
		fail[cur]=fail[q]=cln;
	}
	now=cur;
}
void dfs1(int u) {
	siz[u]=1;
	for (auto& v:to[u]) {
		dep[v]=dep[u]+1;
		back[v]=u;
		dfs1(v);
		if (siz[v]>msiz[u]) msiz[u]=siz[v], mson[u]=v;
		siz[u]+=siz[v];
	}
}
void dfs2(int u, int t) {
	rk[id[u]=++all]=u; top[u]=t;
	if (!mson[u]) return ;
	dfs2(mson[u], t);
	for (auto& v:to[u]) if (v!=mson[u])
		dfs2(v, v);
}
namespace parent{
	vector<int> to[N];
	int siz[N], msiz[N], mson[N], dep[N], top[N], back[N];
	void dfs1(int u) {
		siz[u]=1;
		for (auto& v:to[u]) {
			dep[v]=dep[u]+1;
			back[v]=u;
			dfs1(v);
			if (siz[v]>msiz[u]) msiz[u]=siz[v], mson[u]=v;
			siz[u]+=siz[v];
		}
	}
	void dfs2(int u, int t) {
		top[u]=t;
		if (!mson[u]) return ;
		dfs2(mson[u], t);
		for (auto& v:to[u]) if (v!=mson[u])
			dfs2(v, v);
	}
	int lca(int a, int b) {
		while (top[a]!=top[b]) {
			if (dep[top[a]]<dep[top[b]]) swap(a, b);
			a=back[top[a]];
		}
		return dep[a]<dep[b]?a:b;
	}
	void build() {
		for (int i=1; i<=tot; ++i) to[fail[i]].pb(i);
		dfs1(0), dfs2(0, 0);
	}
}
int bit2[N];
inline void upd2(int i, int dat) {for (; i<=all; i+=i&-i) bit2[i]+=dat;}
inline int query2(int i) {int ans=0; for (; i; i-=i&-i) ans+=bit2[i]; return ans;}
#define tl(p) tleft[p]
#define tr(p) tright[p]
#define pushup(p) sum[p]=sum[p<<1]+sum[p<<1|1]
ll dat[N<<2], sum[N<<2], val[N];
int tleft[N<<2], tright[N<<2], tag[N<<2];
inline void spread(int p) {
	sum[p<<1]+=dat[p<<1]*tag[p]; tag[p<<1]+=tag[p];
	sum[p<<1|1]+=dat[p<<1|1]*tag[p]; tag[p<<1|1]+=tag[p];
	tag[p]=0;
}
void build(int p, int l, int r) {
	tl(p)=l; tr(p)=r;
	if (l==r) {dat[p]=val[rk[l]]; return ;}
	int mid=(l+r)>>1;
	build(p<<1, l, mid);
	build(p<<1|1, mid+1, r);
	dat[p]=dat[p<<1]+dat[p<<1|1];
}
ll mix(int p, int l, int r, int val) {
	if (l<=tl(p)&&r>=tr(p)) {ll tem=sum[p]; sum[p]+=dat[p]*val; tag[p]+=val; return tem;}
	if (tag[p]) spread(p);
	int mid=(tl(p)+tr(p))>>1; ll ans=0;
	if (l<=mid) ans+=mix(p<<1, l, r, val);
	if (r>mid) ans+=mix(p<<1|1, l, r, val);
	pushup(p);
	return ans;
}
ll mix_query(int u, int val) {
	ll ans=0;
	while (u) {
		ans+=mix(1, id[top[u]], id[u], val);
		upd2(id[top[u]], val), upd2(id[u]+1, -val);
		u=back[top[u]];
	}
	return ans;
}

signed main()
{
	freopen("elixir.in", "r", stdin);
	freopen("elixir.out", "w", stdout);

	scanf("%s%d", s+1, &q);
	n=strlen(s+1);
	init();
	for (int i=1; i<=n; ++i) insert(s[i]), pos[i]=now;
	for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	// cout<<"pos: "; for (int i=1; i<=n; ++i) cout<<pos[i]<<' '; cout<<endl;
	for (int u=0; u<=tot; ++u)
		for (int i=0; i<4; ++i) if (len[tr[u][i]]==len[u]+1)
			to[u].pb(tr[u][i]); //, cout<<"add: "<<u<<' '<<tr[u][i]<<endl;
	dep[0]=1; dfs1(0); dfs2(0, 0);
	parent::build();
	for (int i=1; i<=tot; ++i) val[i]=len[i]-len[back[i]];
	build(1, 1, all);
	for (int i=1,x,y; i<=q; ++i) {
		// cout<<"i: "<<i<<endl;
		scanf("%d%d", &x, &y);
		int u=parent::lca(pos[x], pos[y]);
		if (vis[u]) {
			add(id[u], -1);
			ans+=1ll*len[u]*(query2(id[u])-query(id[u], id[u]+siz[u]-1));
			ans-=mix_query(u, -1);
		}
		else {
			ans+=1ll*len[u]*(query(id[u], id[u]+siz[u]-1)-query2(id[u]));
			add(id[u], 1);
			ans+=mix_query(u, 1);
		}
		vis[u]^=1;
		printf("%lld\n", ans);
	}

	return 0;
}
posted @ 2022-07-20 15:51  Administrator-09  阅读(1)  评论(0编辑  收藏  举报