题解 唱诗

传送门

只会到单次询问 \(O(|答案子串||\sum|)\) 的做法
建出广义 SAM 来直接每次把字典序第 \(k\) 小子串找出来就好了

正解是trick科技:

  • 关于 DAG 剖分:
    \(f_i\) 为从 \(i\) 出发的路径数
    对每个点挑选 \(f\) 最大的后继作为重后继
    这样每跳一次轻边路径数减半,所以是 \(O(\log 总路径数)\)
    支持 DAG 上路径查询,但无法带修(可以存在一个点是多个点的重后继)
    可拓展性很低,貌似只能用在类似快速找字典序第 k 大子串这种地方
    要不然出题人要怎么告诉你要查哪条路径

于是发现在一个节点 \(u\) 时,仅当 \(k\in[l_u, r_u]\) 时要走重后继(\(l, r\) 是能算的)
然后这个东西是可以倍增的,维护跳 \(2^i\) 步的范围,到的点以及一些其它信息
还是利用跳一次轻边路径数减半来约束复杂度
复杂度 \(O((n+q)(|\sum|+\log n)\log n)\)在某 OJ 本机 1.8 s 时限 2.5 s 卡不过去
上面复杂度写的不太对,\(n\) 部分好像是一个 \(\log\),而且可以前缀和二分出要跳的后缀是哪个
那么具体是 \(O(n(|\sum|+\log n)+q(\log n+\log{|\sum|})\log n)\)反正还是过不去就是了

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 400010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

int n, m;
char c[5];
ll dis[21][N], f[N];
queue<pair<int, int>> q;
vector<pair<int, int>> son[N];
int suf[21][N], val[21][N], msiz[N], mson[N], deg[N];
int tr[N][26], len[N], fail[N], cnt[N], tem[N], dep[N], lg[N], tot;
struct range{ll l, r;}lim[21][N];
inline range operator + (range a, ll t) {return {a.l+t, a.r+t};}
inline range operator & (range a, range b) {return {max(a.l, b.l), min(a.r, b.r)};}
void init() {fail[0]=-1;}
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][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<26; ++i) tr[cln][i]=len[tr[q][i]]?tr[q][i]:0;
		for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
		fail[cur]=fail[q]=cln;
	}
	return cur;
}

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

	scanf("%d%d", &n, &m);
	init(); tot=n-1;
	memset(mson, -1, sizeof(mson));
	for (int i=1,u,v; i<n; ++i) {
		scanf("%d%d%s", &u, &v, c);
		tr[u-1][*c-'a']=v-1;
	}
	for (int i=0; i<26; ++i) if (tr[0][i]) q.push({i, 0});
	while (q.size()) {
		pair<int, int> u=q.front(); q.pop();
		int now=insert(u.fir, u.sec);
		for (int i=0; i<26; ++i) if (tr[now][i]) q.push({i, now});
	}
	for (int i=1; i<=tot; ++i) ++cnt[len[i]];
	for (int i=1; i<=n; ++i) cnt[i]+=cnt[i-1];
	for (int i=1; i<=tot; ++i) tem[cnt[len[i]]--]=i;
	for (int i=1; i<=tot+1; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for (int i=tot; ~i; --i) {
		int u=tem[i]; ++f[u]; dep[u]=1;
		for (int j=0; j<26; ++j) if (tr[u][j]) {
			f[u]+=f[tr[u][j]]; dep[u]=max(dep[u], dep[tr[u][j]]+1);
			if (f[tr[u][j]]>msiz[u]) msiz[u]=f[tr[u][j]], mson[u]=j;
		}
		ll sum=1, l=2, r=INF;
		for (int j=0; j<26; ++j) if (tr[u][j]) {
			if (j<mson[u]) sum+=f[tr[u][j]], l+=f[tr[u][j]];
			if (j>mson[u]) {r=sum+f[tr[u][mson[u]]]; break;}
		}
		if (~mson[u]) suf[0][u]=tr[u][mson[u]], dis[0][u]=sum, val[0][u]='a'+mson[u], lim[0][u]={l, r};
		for (int i=1; dep[u]>=1<<i; ++i) {
			suf[i][u]=suf[i-1][suf[i-1][u]];
			dis[i][u]=dis[i-1][u]+dis[i-1][suf[i-1][u]];
			val[i][u]=val[i-1][u]+val[i-1][suf[i-1][u]];
			lim[i][u]=lim[i-1][u]&(lim[i-1][suf[i-1][u]]+dis[i-1][u]);
		}
	}
	// cout<<"suf: "; for (int i=0; i<=tot; ++i) cout<<suf[0][i]<<' '; cout<<endl;
	ll k;
	for (int i=1; i<=m; ++i) {
		// cout<<"i: "<<i<<endl;
		scanf("%lld", &k);
		if (++k>f[0]) {puts("-1"); continue;}
		int u=0, ans=0;
		while (k) {
			// cout<<"u: "<<u<<' '<<k<<endl;
			for (int i=lg[dep[u]]-1; ~i; --i) if (lim[i][u].l<=k&&k<=lim[i][u].r)
				k-=dis[i][u], ans+=val[i][u], u=suf[i][u];
			if (--k==0) break;
			for (int i=0; i<26; ++i) if (tr[u][i]) {
				if (f[tr[u][i]]>=k) {u=tr[u][i]; ans+='a'+i; break;}
				else k-=f[tr[u][i]];
			}
		}
		printf("%d\n", ans);
	}
	
	return 0;
}
posted @ 2022-07-20 14:45  Administrator-09  阅读(4)  评论(0编辑  收藏  举报