P8147 [JRKSJ R4] Salieri 题解

题意:给定 nn 个字符串 sis_i,每个字符串有权值 viv_iqq 次询问,每次给定一个字符串 SS 和一个数 kk。定义 cnticnt_isis_iSS 中出现次数。求 cnti×vicnt_i \times v_i 的第 kk 大的值。

范围:n,m105n, m \leq 10^5si,Si5×105\sum \left| s_i \right|, \sum \left| S_i \right| \leq 5 \times 10^5knk \leq n。字符集只有 {a,b,c,d}\{\texttt{a,b,c,d}\}。时限 22 秒。

解法:

建出 AC 自动机的 failfail 树后,就等价于若干个点,支持到根路径每个点 cntcnt11。修改后求 cnti×vicnt_i \times v_i 的第 kk 大。不存在字符串末尾的点视为 k=0k=0 即可。

直接做不支持多组询问。不太对。考虑二分。就变成了询问有多少个点 cnti×vi>xcnt_i \times v_i > x。但是还是不太好做。

多次询问,每次给若干个点。这不是虚树吗?虚树上每条边上的点的 cntcnt 值都相同,等于这条边下面那个点子树和。于是这些边上要满足 vi>xszvv_i > \dfrac{x}{sz_v}。就变成了询问链上点权大于某数个数的点数。直接主席树维护即可。由于 kk 个点建虚树只有 O(k)O(k) 条边,总复杂度 2log2\log

关于虚树:

把树视为有根树。

建立虚树需要有一个关键点集合 SS,通常要把根加入 SS,设为 S={s1,s2,,sk}S=\{s_1,s_2,\cdots,s_k\}。虚树只包含所有关键点和任意一个关键点子集的 LCA。连边方式按照祖先关系。

先说怎么求虚树点集。

Lemma 1.\text{Lemma 1.} 任意点集 SS 的 LCA 为其中 DFS 序最小的和最大的两个点的 LCA。

Proof.\text{Proof.} 记 DFS 序列最小和最大两点分别为 uuvv。容易知道点集 SS 的 LCA 必然是 u,vu,v 的 LCA 的祖先。我们只需要证明点集 SS 中任意一个点都在 u,vu,v 的 LCA 子树内。显然假如有一个不在子树内,那 DFS 序最小和最大的必然不是 u,vu,v。证毕。

于是我们只需要求出任意两点 LCA 加入虚树点集即可。然而这个复杂度还是 O(k2logn)O(k^2 \log n) 的,不能接受。

Lemma 2.\text{Lemma 2.} 将点集按 DFS 序排序,取相邻两个点的 LCA 加入虚树集合即可。

Proof.\text{Proof.} 本质等价于证明对于任意 i<ji < jsis_isjs_j 的 LCA 都被加入过虚树。

归纳证明,当 ji=1j - i =1 时,显然。考虑 ji>1j-i>1。我们知道 sis_isj1s_{j-1} 的 LCA 必然被加入过。如果 sjs_j 不在这两个点 LCA 子树内,那么考虑 sj1s_{j-1}sjs_j 的 LCA 即为 sis_isjs_j 的 LCA。否则 sis_isjs_j 的 LCA 在之前就加入过了。

证毕。

然后我们就做到了在 O(klogn)O(k \log n) 的复杂度内求出虚树点集。然后将虚树内点按 DFS 序排序,相邻两点的 LCA 连向第二个点即可。

容易发现每条边上的点对应的 cntcnt 都是一样的。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
using namespace std;

using ll = long long;

const int N = 5e5 + 5;

int n, m;
string s[N];
ll v[N];
int pos[N];
vector<int> G[N];

class ACAM
{
public:
	int son[N][4];
	int fail[N];
	int idx;
	int flag[N];
	int ins(string s)
	{
		int u = 1;
		for (auto& j : s)
		{
			int nj = j - 'a';
			if (!son[u][nj]) son[u][nj] = ++idx;
			u = son[u][nj];
		}
		flag[u]++;
		return u;
	}
	void getfail()
	{
		queue<int> q;
		fail[1] = 1;
		for (int i = 0; i < 4; i++)
		{
			fail[son[1][i]] = 1;
			if (son[1][i]) q.push(son[1][i]);
		}
		while (q.size())
		{
			int u = q.front();
			q.pop();
			for (int i = 0; i < 4; i++)
			{
				if (!son[u][i]) son[u][i] = son[fail[u]][i];
				else
				{
					fail[son[u][i]] = son[fail[u]][i];
					q.push(son[u][i]);
				}
			}
		}
		for (int i = 1; i <= idx; i++) if (!fail[i]) fail[i] = 1;
		for (int i = 2; i <= idx; i++)
		{
			G[fail[i]].emplace_back(i);
		}
	}
}ac;

vector<int> total;
int rt[N];
vector<int> nv[N];

class Chairman_tree
{
public:
	int idx;
	struct Node
	{
		int lson, rson;
		ll sum;
	}tr[N * 21];
	void pushup(int u)
	{
		tr[u].sum = tr[tr[u].lson].sum + tr[tr[u].rson].sum;
	}
	int update(int p, int l, int r, int x)
	{
		int u = ++idx;
		tr[u] = tr[p];
		if (l == r)
		{
			tr[u].sum++;
			return u;
		}
		int mid = l + r >> 1;
		if (x <= mid) tr[u].lson = update(tr[p].lson, l, mid, x);
		else tr[u].rson = update(tr[p].rson, mid + 1, r, x);
		pushup(u);
		return u;
	}
	ll qry(int derricklo, int lt, int lt2, int nl, int nr, int l, int r)
	{
		if (l > r) return 0;
		if (nl >= l && nr <= r) return tr[lt].sum + tr[lt2].sum - tr[derricklo].sum;
		int mid = nl + nr >> 1;
		ll res = 0;
		if (l <= mid) res = qry(tr[derricklo].lson, tr[lt].lson, tr[lt2].lson, nl, mid, l, r);
		if (r > mid) res += qry(tr[derricklo].rson, tr[lt].rson, tr[lt2].rson, mid + 1, nr, l, r);
		return res;
	}
}sgt;

int lt[N], dl;
int faf[N][21];
int dep[N];

void dfs(int u, int fa, int d)
{
	dep[u] = d;
	lt[u] = ++dl;
	if (nv[u].size())
	{
		rt[u] = sgt.update(rt[fa], 0, N, nv[u][0]);
		for (int i = 1; i < nv[u].size(); i++) rt[u] = sgt.update(rt[u], 0, N, nv[u][i]);
	}
	else
	{
		rt[u] = rt[fa];
	}
	for (auto& j : G[u]) dfs(j, u, d + 1);
}

class TreeCut
{
public:
	int top[N], sz[N], fa[N], son[N], dep[N];
	void dfs1(int u, int f)
	{
		fa[u] = f;
		sz[u] = 1;
		dep[u] = dep[f] + 1;
		for (auto& j : G[u])
		{
			if (j != f)
			{
				dfs1(j, u);
				sz[u] += sz[j];
				if (sz[son[u]] < sz[j]) son[u] = j;
			}
		}
	}
	void dfs2(int u, int f)
	{
		top[u] = f;
		if (!son[u]) return;
		dfs2(son[u], f);
		for (auto& j : G[u])
		{
			if (j != fa[u] && j != son[u]) dfs2(j, j);
		}
	}
	inline int LCA(int u, int v)
	{
		while (top[u] ^ top[v])
		{
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			u = fa[top[u]];
		}
		return (dep[u] < dep[v] ? u : v);
	}
}tc;

inline bool cmp(int x, int y)
{
	return lt[x] < lt[y];
}

vector<int> NG[N];
int flag[N];
int sz[N];
int cnt;

void vtdfs(int u)
{
	sz[u] = flag[u];
	for (auto& j : NG[u])
	{
		vtdfs(j);
		sz[u] += sz[j];
	}
}

ll nk;

void calcans(int u, int fa)
{
	for (auto& j : NG[u])
	{
		calcans(j, u);
		// val[i] * sz[j] > nk
		// val[i] > nk / sz[j]
		if (sz[j] && nk / sz[j] + 1 < N)
		{
			ll nval = nk / sz[j] + 1;
			//cnt -= (1ll * nv[j] * sz[j] > nk);
			cnt += sgt.qry(rt[u], 0, rt[j], 0, N, nval, N);
		}
	}
}

int check(ll k)
{
	nk = k;
	cnt = 0;
	vtdfs(1);
	calcans(1, 0);
	return cnt;
}

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m;
	ac.idx = 1;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i] >> v[i];
		pos[i] = ac.ins(s[i]);
		nv[pos[i]].emplace_back(v[i]);
	}
	ac.getfail();
	dfs(1, 0, 1);
	tc.dfs1(1, 0);
	tc.dfs2(1, 1);
	while (m--)
	{
		string s;
		int k;
		cin >> s >> k;
		total.clear();
		int u = 1;
		for (auto& j : s)
		{
			u = ac.son[u][j - 'a'];
			total.emplace_back(u);
			flag[u]++;
		}
		sort(total.begin(), total.end(), cmp);
		// solve
		vector<int> vec;
		for (int i = 0; i + 1 < total.size(); i++)
		{
			vec.emplace_back(total[i]);
			vec.emplace_back(tc.LCA(total[i], total[i + 1]));
		}
		vec.emplace_back(total.back());
		vec.emplace_back(1);
		sort(vec.begin(), vec.end(), cmp);
		vec.erase(unique(vec.begin(), vec.end()), vec.end());
		for (int i = 0; i + 1 < vec.size(); i++)
		{
			NG[tc.LCA(vec[i], vec[i + 1])].emplace_back(vec[i + 1]);
		}
		ll l = 0ll, r = (ll)8e8, ans = 0ll;
		while (l <= r)
		{
			ll mid = l + r >> 1ll;
			if (check(mid) < k) r = mid - 1, ans = mid;
			else l = mid + 1;
		}
		for (auto& j : total) flag[j] = 0;
		for (auto& j : vec) NG[j].clear(), sz[j] = 0;
		cout << ans << "\n";
	}
	return 0;
}
posted @ 2024-03-08 12:05  HappyBobb  阅读(13)  评论(0编辑  收藏  举报  来源