vp + 补题 + 随机做题 记录十

2026“钉耙编程”中国大学生算法设计暑期联赛(4) 1012 Yet Another Suffix Array Problem

link

支配集技巧,或者叫候选集,非常高妙。

先考虑求第一问。lcp 本质上是区间 \(\min\),所以等价于 \(\max_{l\leq i<j\leq r}\min\{\min_{rk[i]\leq k<rk[j]}\{ht[k]\}, r-j\}\)

忽略 \(ht\) 的实际含义,也先不考虑最后和 \(r-j\)\(\min\),我们来看简化后的 \(\max-\min\) 问题怎么做。

对于固定的区间最小值 \(H\)\(rk[i]\),我们发现,如果 \(j_1\)\(j_2\) 都能满足区间最小值为 \(H\),且 \(rk[j_1]<rk[j_2]\),那么所有 \(j_2\) 能成为答案的区间 \([l, r]\), 一定也有 \(l\leq j_1\leq r\),我们就称 \(j_1\) 支配了 \(j_2\)

根据区间最小值的单调性,我们可以从大到小枚举 \(ht\) 数组的元素,做启发式合并,得到所有的支配区间。不难发现,仅考虑这些支配区间,足以求解原问题。于是我们把 \(n^2\) 个区间减少到了只剩 \(O(n\log n)\) 个。

如果没有和 \(r-j\)\(\min\),可以直接做二维数点。有这个要求也可以二维数点,总之可以做到两个 \(\log\)

剩下的构造方案需要根据 lcp 的性质分类讨论,比较无聊就省略了。

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
typedef pair<int,int> pii;
#define fi first
#define se second
#define mapa make_pair
typedef long double ld;
typedef unsigned long long ull;
#define ep emplace_back
template <typename T>inline void read(T &x){
	x=0;char c=getchar();bool f=0;
	for(;c<'0'||c>'9';c=getchar()) f|=(c=='-');
	for(;c>='0'&&c<='9';c=getchar())
	x=(x<<1)+(x<<3)+(c^48);
	x=(f?-x:x);
}
const int N=4e5+5;
int Test, n, m;
struct sgt{
	int rt[N];
	int idx;
	int sz[N*60], ls[N*60], rs[N*60];
	int gen(int p){
		++idx; 
		sz[idx]=sz[p];
		ls[idx]=ls[p];
		rs[idx]=rs[p];
		return idx;
	}
	void add(int &p, int q, int l, int r, int x){
		p=gen(q);
		++sz[p];
		if(l==r) return ;
		int mid=(l+r)>>1;
		if(x<=mid) add(ls[p], ls[q], l, mid, x);
		else add(rs[p], rs[q], mid+1, r, x);
	}
	int lim;
	int fnd(int p, int q, int l, int r, int L, int R){
		if(L<=l&&r<=R){
			if(lim>sz[p]-sz[q]){
				lim-=sz[p]-sz[q];
				return -1;
			}
			if(l==r) return l;
			int mid=(l+r)>>1;
			if(lim<=sz[ls[p]]-sz[ls[q]]) return fnd(ls[p], ls[q], l, mid, L, R);
			lim-=sz[ls[p]]-sz[ls[q]];
			return fnd(rs[p], rs[q], mid+1, r, L, R);
		}
		int mid=(l+r)>>1, ret=-1;
		if(L<=mid) ret=fnd(ls[p], ls[q], l, mid, L, R);
		if(R>mid&&ret==-1) ret=fnd(rs[p], rs[q], mid+1, r, L, R);
		return ret;
	}
}T;
char s[N];
int x[N], y[N], c[N], sa[N], ht[N], rk[N];
void get_sa(){
	for(int i=1; i<=n*2; ++i) y[i]=0;
	for(int i=1; i<='z'; ++i) c[i]=0;
	for(int i=1; i<=n; ++i) c[x[i]=s[i]]++;
	int m='z';
	for(int i=2; i<=m; ++i) c[i]+=c[i-1];
	for(int i=n; i; --i) sa[c[x[i]]--]=i;
	for(int k=1; k<=n; k<<=1){
		int num=0;
		for(int i=n-k+1; i<=n; ++i) y[++num]=i;
		for(int i=1; i<=n; ++i){
			if(sa[i]>k) y[++num]=sa[i]-k;
		}
		for(int i=1; i<=m; ++i) c[i]=0;
		for(int i=1; i<=n; ++i) c[x[i]]++;
		for(int i=2; i<=m; ++i) c[i]+=c[i-1];
		for(int i=n; i; --i) sa[c[x[y[i]]]--]=y[i], y[i]=0;
		for(int i=1; i<=n; ++i) swap(x[i], y[i]);
		x[sa[1]]=1; num=1;
		for(int i=2; i<=n; ++i) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
		if(num==n) break;
		m=num;
	}
}
void get_ht(){
	for(int i=1; i<=n; ++i) rk[sa[i]]=i;
	for(int i=1, k=0; i<=n; ++i){
		if(rk[i]==1) continue;
		if(k) --k;
		int j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;
		ht[rk[i]]=k;
	}
}
int stt[20][N], lg[N];
void build_stt(){
	for(int i=2; i<=n; ++i) lg[i]=lg[i>>1]+1;
	for(int i=1; i<=n; ++i) stt[0][i]=ht[i];
	for(int t=1; t<=lg[n]; ++t){
		for(int i=1; i+(1<<t)-1<=n; ++i){
			stt[t][i]=min(stt[t-1][i], stt[t-1][i+(1<<(t-1))]);
		}
	}
}
int lcp(int x, int y){
	if(x==y) return n-x+1;
	x=rk[x]; y=rk[y];
	if(x>y) swap(x, y);
	++x;
	int t=lg[y-x+1];
	return min(stt[t][x], stt[t][y-(1<<t)+1]);
}
int ql[N], qr[N];
set<int> st[N]; int rt[N];
int p[N];
vector<pii> bin[N];
struct BIT{
	pii tr[N];
	void clr(){
		for(int i=1; i<=n+1; ++i) tr[i]=mapa(-1e9, 0);
	}
	void upd(int x, pii v){
		for(; x<=n+1; x+=(x&-x)) tr[x]=max(tr[x], v);
	}
	pii get(int x){
		pii ret=mapa(-1e9, 0);
		for(; x; x-=(x&-x)) ret=max(ret, tr[x]);
		return ret;
	}
}T1, T2;
vector<int> qry[N];
pii ans[N];
void solve(){
	read(n); read(m);
	scanf("%s", s+1);
	get_sa();
	get_ht();
	build_stt();
	for(int i=1; i<=n; ++i) qry[i].clear();
	for(int i=1; i<=m; ++i) {
		read(ql[i]), read(qr[i]);
		qry[ql[i]].ep(i);
	}
	for(int i=1; i<=n; ++i) st[i].clear(), st[i].insert(i), rt[i]=i, p[i]=i, bin[i].clear();
	sort(p+2, p+n+1, [&](int x, int y){return ht[x]<ht[y];});
	for(int i=n; ht[p[i]]; --i){
		int l=rt[sa[p[i]-1]], r=rt[sa[p[i]]];
		if(st[l].size()>st[r].size()) swap(l, r);
		for(auto t:st[l]){
			set<int>::iterator it=st[r].lower_bound(t);
			if(it!=st[r].end()){
				bin[t].ep(*it, ht[p[i]]);
			}
			if(it!=st[r].begin()){
				--it;
				bin[*it].ep(t, ht[p[i]]);
			}
		}
		for(auto t:st[l]) st[r].insert(t), rt[t]=r;
	}
	T1.clr(); T2.clr();
	T.idx=0;
	for(int i=1; i<=n+1; ++i) T.rt[i]=0;
	T.add(T.rt[n], T.rt[n+1], 1, n, rk[n]);
	for(int l=n-1; l>=1; --l){
		T.add(T.rt[l], T.rt[l+1], 1, n, rk[l]);
		for(auto [r, v]:bin[l]){
			T1.upd(r+v, mapa(v, -min(rk[l], rk[r])));
			T2.upd(n+2-(r+v), mapa(-r, -min(rk[l], rk[r])));
		}
		for(auto id:qry[l]){
			int r=qr[id];
			pii v1=T1.get(r+1), v2=T2.get(n+1-r);
			v2.fi+=r+1;
			ans[id]=max(v1, v2);
		}
	}
	for(int i=1; i<=m; ++i){
		int H=ans[i].fi;
		int t=-ans[i].se, id=sa[t];
		int lp=t, rp=t;
		int l=ql[i], r=qr[i];
		{
			int lv=1, rv=t-1, mid;
			while(lv<=rv){
				mid=(lv+rv)>>1;
				if(lcp(sa[mid], id)>=H) {
					lp=mid; rv=mid-1;
				}
				else{
					lv=mid+1;
				}
			}
		}
		{
			int lv=t+1, rv=n, mid;
			while(lv<=rv){
				mid=(lv+rv)>>1;
				if(lcp(sa[mid], id)>=H) {
					rp=mid; lv=mid+1;
				}
				else{
					rv=mid-1;
				}
			}
		}
		int ansl, ansr;
		if(rk[r-H+1]>=lp&&rk[r-H+1]<=rp){
			ansl=r-H+1;
			T.lim=1;
			ansr=sa[T.fnd(T.rt[l], T.rt[r-H+1], 1, n, lp, rp)];
		}
		else{
			T.lim=1;
			ansl=sa[T.fnd(T.rt[l], T.rt[r-H+1], 1, n, lp, rp)];
			T.lim=2;
			ansr=sa[T.fnd(T.rt[l], T.rt[r-H+1], 1, n, lp, rp)];			
		}
		printf("%d %d %d\n", H, ansl, ansr);
	}
}
int main(){
	// freopen("D:\\nya\\acm\\A\\test.in","r",stdin);
	// freopen("D:\\nya\\acm\\A\\test.out","w",stdout);
	read(Test);
	while(Test--){
		solve();
	}
	return 0;
}
posted @ 2026-09-03 19:21  Displace  阅读(7)  评论(0)    收藏  举报