洛谷 P10716

传送门

并不难的串串。两只 \(\log\) 是随便做的,这里讨论一只 \(\log\) 怎么做。

由于老师讲的 zlt 做法,个人代码也借鉴了 zlt 的清新写法,还是不交 tj 了。

首先将题意翻译成求多少个 \(S\) 的前缀串可以在前缀 \(i\) 中不重叠地出现 \(k\) 次,且最后一次出现顶到了 \(i\)

自然地,对于每个前缀串 \(S[1,i]\),求出第一个满足其能不重叠出现 \(j\) 次的位置 \(f_{i,j}\)。这可以做到 \(O(n\alpha(n)\ log\ n)\)

先跑一遍 exkmp 求出所有 \(LCP(S,S[j,n])\) 记为 \(z_j\),然后枚举前缀串 \(S[1,i]\) 并贪心向右匹配,问题变成了 \(n\ log\ n\) 次查询,每次查询要求出位置 \(p\) 及其右边第一个满足 \(z_j\ge i\) 的位置 \(j\)。于是想到对 \(i\) 扫描线,并开一个并查集维护合法的 \(j\),支持删除和后继查询,于是就做到了上述的复杂度。

求出了所有 \(f_{i,j}\) 之后就可以开始考虑每次询问。由于最后一次出现必须恰好顶到 \(i\),考虑跑一遍 kmp 建出失配树,那么相当于满足条件的串一定在 \(i\) 的祖先中。

然后我们又可以注意到一个关键性质:显然越往上跳就越能满足条件,也就是说满足条件的串一定是 \(i\) 到根的链的上半段。所以考虑直接倍增找出这一段,利用 \(f_{i,j}\) 即可 \(O(1)\) 判断合法性,于是这一部分总复杂度 \(O(q\ log\ n)\)

总复杂度为 \(O(n\alpha(n)\ log\ n+q\ log\ n)\)

code

#include <bits/stdc++.h>
//taskkill /f /im 未命名1.exe
#define ED cerr<<endl;
#define TS cerr<<"I AK IOI"<<endl;
#define cr(x) cerr<<x<<endl;
#define cr2(x,y) cerr<<x<<" "<<y<<endl;
#define cr3(x,y,z) cerr<<x<<" "<<y<<" "<<z<<endl;
#define cr4(x,y,z,w) cerr<<x<<" "<<y<<" "<<z<<" "<<w<<endl;
#define print(a,l,r) for(int i=l;i<=r;++i) cerr<<a[i]<<' ';puts("");
#define popcnt __builtin_popcount
#define all(s) s.begin(),s.end()
#define bstring basic_string
//#define add(x,y) (x+=y)%=mod
#define pii pair<int,int>
#define epb emplace_back
#define pb push_back
#define mk make_pair
#define ins insert
#define fi first
#define se second
#define ll long long
//#define ull unsigned long long
using namespace std;
const int N=2e5+5,INF=2e9,mod=1e9+7;
int t,n,m;
char s[N];
int z[N];vector<int> S[N];
int p[18][N],dep[N],fa[N];
vector<int> val[N];

int find(int u) {
	if(u==fa[u]) return u;
	else return fa[u]=find(fa[u]);
}

void kmp() {
	for(int i=2,j=0;i<=n;++i) {
		while(j&&s[i]!=s[j+1]) j=p[0][j];
		if(s[i]==s[j+1]) ++j;
		p[0][i]=j;
	}
}

void exkmp() {
	int l=1,r=0;z[1]=n;
	for(int i=2;i<=n;++i) {
		if(i>r) z[i]=0;
		else z[i]=min(z[i-l+1],r-i+1);
		while(i+z[i]<=n&&s[z[i]+1]==s[i+z[i]]) ++z[i];
		if(i+z[i]-1>r) l=i,r=i+z[i]-1;
	}
}

void sol() {
	scanf("%d",&n);
	scanf("%s",s+1);
	kmp(),exkmp();fa[n+1]=n+1;
	for(int i=1;i<=n;++i) {
		S[z[i]].epb(i),fa[i]=(z[i]?i:i+1);
	}
	for(int i=1;i<=n;++i) {
		int p=1;
		while(p+i-1<=n) {
			val[i].epb(p+i-1);
			p=find(p+i);
		} 
		for(auto it:S[i]) {
			fa[it]=it+1;
		}
	}
	for(int i=1;i<=n;++i) {
		dep[i]=dep[p[0][i]]+1;
	}
	for(int i=1;i<18;++i) {
		for(int j=1;j<=n;++j) {
			p[i][j]=p[i-1][p[i-1][j]];
		}
	}
	int q,id,k;scanf("%d",&q);
	while(q--) {
		scanf("%d%d",&id,&k);
		if(k==1) {
			puts("1");continue;
		}
		int ps=id;
		for(int j=17;j>=0;--j) {
			int to=p[j][ps];
			if(to&&(val[to].size()<k||val[to][k-1]>id)) {
				ps=to;
			}
		}
		printf("%d\n",dep[ps]-1);
	}
}

int main()
{
	t=1;
	while(t--) {
		sol();
	}
	return 0;
}
posted @ 2025-07-21 20:21  Oier_szc  阅读(8)  评论(0)    收藏  举报