[EC Final 2022] Minimum Suffix

又没做出来,思路卡在如何处理 Duval 算法带给我们的信息上。

Lyndon 题。首先这个 \(p_i\) 其实是告诉了我们每一个前缀的 Lyndon 分解的,因为我们知道 Lyndon 分解有这样一个求法,对于一个完全是由 Lyndon 串组成的字符串的分解(不是 Lyndon 分解),取出相邻两项 \(u,v\) 满足 \(u<v\),合并这两个串,不断做下去就得到了原串的 Lyndon 分解。而最小后缀其实就是最后一个 Lyndon word,这样维护一个栈,那么每次加入一个字符后会合并一段后缀,合并的长度由 \(p_i\) 决定。

如何利用这个信息呢?一个想法是直接 Duval,但是你会发现你几乎没有办法简单刻画类 Lyndon 串。还有你加入一个字符的时候,具体怎么对待这些串还需要看后续它们是否会合并起来,因为后面的 \(p_i\) 也会给前面的决策带来偏序限制。于是我便开了 Sol。

Sol 的想法形如:我们不如考虑一开始就求出整串的 Lyndon 结构,然后对于每一个 Lyndon 串来说分别解决,此时每一个串后续一定会合并起来而且合并成恰好一个串!这样处理时我们压根不用考虑最难搞的 \(s_k>s_j\) 的情况。

处理完单个 Lyndon 串,我们会得到一个由 = 与 < 组成的树形结构。Lyndon 串与串还有 \(\ge\) 的字典序限制。

从后往前贪心,单个 Lyndon 串内从前往后贪心,为了满足串与串之间的限制需要精细调整。调整不难但是极其细节,我调整写挂了,疯狂修才修好了,但是复杂度被我修假了,这份代码那个败笔 goto 语句好像让复杂度没了保证。

#include <cstdio>
#include <algorithm>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=x*10+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int N=3000003;
int p[N],n;
int stk[N],tp;
bool eq[N];int las[N];
int a[N];
void solve(){
	n=read();
	for(int i=1;i<=n;++i) p[i]=read();
	tp=0;
	for(int i=1;i<=n;++i){
		stk[++tp]=i;
		while(tp&&stk[tp]>p[i]) --tp;
		if(!tp||stk[tp]!=p[i]){puts("-1");return;}
	}
	stk[tp+1]=n+1;
	for(int i=1;i<=tp;++i){
		int ps=stk[i];
		for(int x=stk[i]+1;x<stk[i+1];++x){
			las[x]=ps;
			if(p[x]==stk[i]){eq[x]=0,ps=p[x];continue;}
			if(p[x]-x==p[ps]-ps){eq[x]=1,++ps;continue;}
			puts("-1");return;
		}
	}
	for(int i=tp;i;--i){
		a[stk[i]]=1;
		for(int x=stk[i]+1;x<stk[i+1];++x){
			if(eq[x]) a[x]=a[las[x]];
			else a[x]=a[las[x]]+1;
		}
tmp:
		if(i<tp){
			int cmp=0,ps=0;
			for(int x=stk[i],y=stk[i+1];x<stk[i+1]&&y<stk[i+2];++x,++y){
				if(a[x]>a[y]){cmp=1;ps=x;break;}
				if(a[x]<a[y]){cmp=-1;ps=x;break;}
			}
			if(!cmp){
				if(stk[i+1]-stk[i]>stk[i+2]-stk[i+1]) cmp=1;
				if(stk[i+1]-stk[i]<stk[i+2]-stk[i+1]) cmp=-1,ps=stk[i+1]-1;
			}
			if(cmp>=0) continue;
			bool fl=0;
			while(ps>stk[i]&&eq[ps]) --ps,fl=1;
			if(fl){
				a[ps]=a[ps-stk[i]+stk[i+1]]+1;
				for(int x=ps+1;x<stk[i+1];++x){
					if(eq[x]) a[x]=a[las[x]];
					else a[x]=a[las[x]]+1;
				}
				continue;
			}
			else{
				a[ps]=a[ps-stk[i]+stk[i+1]]+(ps==stk[i+1]-1&&ps-stk[i]+stk[i+1]+1<stk[i+2]);
				for(int x=ps+1;x<stk[i+1];++x){
					if(eq[x]) a[x]=a[las[x]];
					else a[x]=a[las[x]]+1;
				}
				goto tmp;
			}
		}
	}
	for(int i=1;i<=n;++i) printf("%d ",a[i]);
	putchar('\n');
}
int main(){
	int tc=read();
	while(tc--) solve();
	return 0;
}
posted @ 2024-08-13 07:40  yyyyxh  阅读(64)  评论(0)    收藏  举报