题解 唱诗
只会到单次询问 \(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;
}