CodeForces - 1073G Yet Another LCP Problem

题意:

给定一个字符串 s s s q q q 次询问,每次给定 k i , l i k_i, l_i ki,li,以及 k i k_i ki 个数 a j a_j aj l i l_i li 个数 b k b_k bk,求 ∑ j = 1 k i ∑ k = 1 l i L C P ( s [ a j ⋯   , n ] ,   s [ b k ⋯   , n ] ) \sum\limits_{j = 1}^{k_i}\sum\limits_{k = 1}^{l_i} LCP(s[a_j \cdots,n],~s[b_k \cdots, n]) j=1kik=1liLCP(s[aj,n], s[bk,n]) ( n , q , ∑ k i , ∑ l i ≤ 2 × 1 0 5 ) (n, q, \sum k_i, \sum l_i \leq 2×10^5) (n,q,ki,li2×105)

链接:

https://codeforces.com/problemset/problem/1073/G

解题思路:

先考虑单次询问如何求解。对串 s s s 的反串建立后缀自动机,则求 l c p lcp lcp 变为求最长公共后缀,答案为两个前缀在自动机上的 l c a lca lca 对应结点的 l e n len len,那么通过枚举 l c a lca lca,设为结点 u u u,则 u u u 贡献的公共后缀长度为 l e n [ u ] − l e n [ p a r [ u ] ] len[u] - len[par[u]] len[u]len[par[u]],对数就是其子树内 a , b a,b a,b 对应前缀的个数之积。

这样求解单次为 O ( n ) O(n) O(n),但实际上用到的点不多,可以建立虚树,在虚树上 d p dp dp 统计。

解题思路:
#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

char s[maxn];
vector<int> G[maxn], P[maxn], pi;
int nxt[maxn][26], par[maxn], len[maxn], pos[maxn];
int dfn[maxn], siz[maxn], son[maxn], dep[maxn], fa[maxn], top[maxn];
int num[maxn][2], stk[maxn], wi[maxn];
int n, m, cnt, last, tim; ll ans;

int add(int l){

	++cnt, len[cnt] = l; return cnt;
}

void init(){

	cnt = 0, last = add(0);
}

void insert(char ch){

	int t = ch - 'a', p = last, cur;
	cur = last = add(len[p] + 1);
	while(p && !nxt[p][t]) nxt[p][t] = cur, p = par[p];
	if(!p) { par[cur] = 1; return; }
	int q = nxt[p][t];
	if(len[q] == len[p] + 1) { par[cur] = q; return; }
	int nq = add(len[p] + 1);
	memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
	par[nq] = par[q], par[q] = par[cur] = nq;
	while(p && nxt[p][t] == q) nxt[p][t] = nq, p = par[p];
}

void dfs1(int u, int f){

	dep[u] = dep[f] + 1, siz[u] = 1, son[u] = 0, fa[u] = f;
	for(auto &v : G[u]){

		dfs1(v, u);
		siz[u] += siz[v];
		son[u] = siz[v] > siz[son[u]] ? v : son[u];
	}
}

void dfs2(int u, int t){

	dfn[u] = ++tim, top[u] = t;
	if(!son[u]) return;
	dfs2(son[u], t);
	for(auto &v : G[u]){

		if(v == son[u] || v == fa[u]) continue;
		dfs2(v, v);
	}
}

int lca(int u, int v){

	while(top[u] != top[v]){

		dep[top[u]] > dep[top[v]] ? u = fa[top[u]] : v = fa[top[v]];
	}
	return dep[u] < dep[v] ? u : v;
}

void link(int u, int v){

	P[u].pb(v), wi[v] = len[v] - len[u];
}

int build(){

	sort(pi.begin(), pi.end(), [](int x, int y){ return dfn[x] < dfn[y]; });
	pi.erase(unique(pi.begin(), pi.end()), pi.end());
	int top; stk[top = 1] = pi[0], pi.erase(pi.begin());
	for(auto &v : pi){

		int lc = lca(stk[top], v);
		while(dep[lc] < dep[stk[top - 1]]) link(stk[top - 1], stk[top]), --top;
		if(lc != stk[top]){

			link(lc, stk[top]), --top;
			if(lc != stk[top]) stk[++top] = lc;
		}
		stk[++top] = v;
	}
	while(--top) link(stk[top], stk[top + 1]);
	return stk[1];
}

void dfs(int u){

	for(auto &v : P[u]){

		dfs(v);
		num[u][0] += num[v][0];
		num[u][1] += num[v][1];
		P[v].clear();
		num[v][0] = num[v][1] = 0;
	}
	ans += num[u][0] * 1ll * num[u][1] * wi[u];
}

int main() {

	ios::sync_with_stdio(0); cin.tie(0);
	cin >> n >> m;
	cin >> s + 1;
	init();
	for(int i = n; i >= 1; --i){

		insert(s[i]);
		pos[i] = last;
	}
	for(int i = 2; i <= cnt; ++i){

		G[par[i]].pb(i);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	while(m--){

		int x, y; cin >> x >> y;
		for(int i = 1; i <= x; ++i){

			int u; cin >> u;
			u = pos[u];
			++num[u][0];
			pi.pb(u);
		}
		for(int i = 1; i <= y; ++i){

			int v; cin >> v;
			v = pos[v];
			++num[v][1];
			pi.pb(v);
		}
		pi.pb(1);
		int rt = build();
		wi[rt] = 0, ans = 0, dfs(1);
		P[rt].clear(), num[rt][0] = num[rt][1] = 0;
		pi.clear();
		cout << ans << "\n";
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值