[JZOJ6241]【NOI2019模擬2019.6.29】字符串【數據結構】【字符串】

Description

給出一個長爲n的字符串SS和一個長爲n的序列aa
定義一個函數f(l,r)f(l,r)表示子串S[l..r]S[l..r]的任意兩個後綴的最長公共前綴的最大值。

現在有q組詢問,每組詢問給出L,R,xL,R,x
你需要找到一個子串S[l,r]S[l,r]滿足[l,r][L,R][l,r]\subset[L,R]f(l,r)xf(l,r)\geq x
同時需要滿足max(a[l..r])max(a[l..r])最小

求這個最小值,無解則輸出-1
n,q50000n,q\leq 50000

Solution

這道題實際上分成兩部分(譴責拼題行爲)

第一部分快速計算f(l,r)
建出SAM,用個set維護right集,在parent樹上啓發式合併,顯然Right集合中我們只需要考慮哪些相鄰的位置有用(不相鄰顯然不優),合併的時候把要插入的位置的前驅以及它們的LCP構成一個三元組,後繼也一樣處理,這樣我們得到了一個可能更新答案的三元組序列(l,x,r)(l,x,r),表示S[l..x]=S[r(xl),r]S[l..x]=S[r-(x-l),r]

觀察對答案的貢獻,它能貢獻的區間滿足R>r,l<=xR>r,l<=x,相當於區間取max以及區間對等差數列取max
維護兩棵線段樹分別來弄,等差數列我們只需要維護合法右端點最大值即可,兩個都是區間取max單點查詢,可以用struct寫在一起

還需要保證右端點的限制,因此對所有二元組按右端點排序建主席樹即可。
區間修改的主席樹似乎比較麻煩?我們發現這題不需要下傳標記,因此直接先將當前位置的操作扔進去再繼承前一個的。

這樣就能夠在O(nlog2n)O(n\log^2n)預處理(啓發式合併),O(logn)O(\log n)計算一個區間的f

第二部分計算答案

顯然只有笛卡爾樹上的n個極大區間是有用的,計算出它們的f

對於查詢區間[L,R][L,R],我們只需要找到被它完全包含的極大區間,然後分別以左右端點開始二分另一個端點即可。

找到包含的區間似乎是個二維問題?

不需要
對於左端點我們二分了一個位置,表示右端點在這左邊的都不合法。

其他的合法區間的右端點一定在這右邊,

此時我們不需要管左端點的限制了,因爲超出去答案顯然更劣。
直接一維區間最大值即可

這一部分也是O(nlog2n)O(n\log^2n)

常數似乎比較大。

Code

代碼足足有5K
略毒瘤。

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=50005;
using namespace std;
char ch[N];
int n,q;
struct node
{
	int l,x,r;
	friend bool operator <(node x,node y)
	{
		return x.r<y.r;
	}
};
vector<node> ap;

namespace SAM
{
	const int R=N<<1;
	int t[R][26],mx[R],m1,ft[R],fs[R],nt[R],dt[R],n2,rf[R];
	set<int> ri[R];
	void link(int x,int y)
	{
		nt[++m1]=fs[x];
		dt[fs[x]=m1]=y;
	}
	void merge(int x,int y,int v)
	{
		if(v==0) return;
		if(ri[rf[x]].size()<ri[rf[y]].size()) swap(rf[x],rf[y]);
		for(int i:ri[rf[y]])
		{
			set<int>::iterator it=ri[rf[x]].lower_bound(i);
			if(it!=ri[rf[x]].end()) ap.push_back((node){i-v+1,i,*it});
			if(it!=ri[rf[x]].begin()) it--,ap.push_back((node){*it-v+1,*it,i});
			ri[rf[x]].insert(i);
		}
	}	
	void dfs(int k)
	{
		int w=0;
		for(int i=fs[k];i;i=nt[i])
		{
			int p=dt[i];
			dfs(p);
			merge(k,p,mx[k]);
		}		
	}
	void make()
	{	
		n2=1;
		int ls=n2;
		fo(i,1,n)
		{
			int c=ch[i]-'a',p=ls;
			mx[ls=++n2]=i;
			rf[ls]=ls;
			ri[ls].insert(i);
			while(p&&!t[p][c]) t[p][c]=ls,p=ft[p];
			if(t[p][c])
			{
				int q=t[p][c];
				if(mx[q]==mx[p]+1) ft[ls]=q;
				else
				{
					mx[++n2]=mx[p]+1;
					ft[n2]=ft[q];
					ft[q]=ft[ls]=n2;
					memcpy(t[n2],t[q],sizeof(t[n2]));
					while(p&&t[p][c]==q) t[p][c]=n2,p=ft[p];
				}
			}
			else ft[ls]=1;
		}
		fo(i,2,n2) link(ft[i],i);
		dfs(1);
	}
}

namespace QS
{
	const int M=10000000;
	
	struct SGT
	{
		int t[M][2],mx[M],rt[N],n1;
		inline int nwp(int &x)
		{ 
			if(!x) x=++n1,t[x][0]=t[x][1]=mx[x]=0;
			return x;
		}
		void modify(int k,int l,int r,int x,int y,int v)
		{
			if(x>y) return;
			if(x<=l&&r<=y) {mx[k]=max(mx[k],v);return;}	
			int mid=(l+r)>>1;
			if(x<=mid) modify(nwp(t[k][0]),l,mid,x,y,v);
			if(y>mid) modify(nwp(t[k][1]),mid+1,r,x,y,v);
		}
		void merge(int &k,int w,int l,int r)
		{
			if(!k) {k=w;return;}
			if(!w) return;
			mx[k]=max(mx[k],mx[w]);
			int mid=(l+r)>>1;
			merge(t[k][0],t[w][0],l,mid);
			merge(t[k][1],t[w][1],mid+1,r);
		}
		int query(int k,int l,int r,int x)
		{
			if(l==r) return mx[k];
			int mid=(l+r)>>1,s=0;
			if(x<=mid) s=query(t[k][0],l,mid,x);
			else s=query(t[k][1],mid+1,r,x);
			return max(s,mx[k]);
		}
	}T1,T2;
	void make()
	{
		SAM::make();
		sort(ap.begin(),ap.end());
		
		int r=ap.size()-1;
		for(int i=1,j=0;i<=n;++i)
		{
			T1.rt[i]=++T1.n1;
			T2.rt[i]=++T2.n1;
			while(j<=r&&ap[j].r<=i)
			{
				T1.modify(T1.rt[i],1,n,1,ap[j].l-1,ap[j].x-ap[j].l+1);
				T2.modify(T2.rt[i],1,n,ap[j].l,ap[j].x,ap[j].x);
				j++;
			}
			T1.merge(T1.rt[i],T1.rt[i-1],1,n);
			T2.merge(T2.rt[i],T2.rt[i-1],1,n);
		}
		n++;
		n--;
	}
	int get(int l,int r)
	{
		return max(T1.query(T1.rt[r],1,n,l),min(T2.query(T2.rt[r],1,n,l),r)-l+1);
	}
}
using QS::get;

int a[N];
struct px
{
	int l,r,v,c;
	friend bool operator <(px x,px y) {return x.v>y.v;}
}aw[N];

int ask[N][3],d[N];
bool cmp(int x,int y) {return ask[x][2]>ask[y][2];}
int lf[N],rf[N],st[N],ans[N];

namespace Tr
{
	int n1,t[N<<1][2],mx[N<<1],mi[N<<1];
	void build(int k,int l,int r)
	{
		mi[k]=1e9;
		if(l==r) {mx[k]=a[l];return;}
		int mid=(l+r)>>1;
		build(t[k][0]=++n1,l,mid),build(t[k][1]=++n1,mid+1,r);
		mx[k]=max(mx[t[k][0]],mx[t[k][1]]);
	}
	void ins(int k,int l,int r,int x,int v)
	{
		if(l==r) {mi[k]=min(mi[k],v);return;}
		int mid=(l+r)>>1;
		if(x<=mid) ins(t[k][0],l,mid,x,v);
		else ins(t[k][1],mid+1,r,x,v);
		mi[k]=min(mi[t[k][0]],mi[t[k][1]]);
	}
	int ds[N];
	void query(int k,int l,int r,int x,int y)
	{
		if(x>y) return;
		if(x<=l&&r<=y) {ds[++ds[0]]=k;return;}
		int mid=(l+r)>>1;
		if(x<=mid) query(t[k][0],l,mid,x,y);
		if(y>mid) query(t[k][1],mid+1,r,x,y);
	}
	int gmax(int l,int r)
	{
		ds[0]=0;
		query(1,1,n,l,r);
		int s=0;
		fo(i,1,ds[0]) s=max(s,mx[ds[i]]);
		return s;
	}
	int gmin(int l,int r)
	{
		ds[0]=0;
		query(1,1,n,l,r);
		int s=1e9;
		fo(i,1,ds[0]) s=min(s,mi[ds[i]]);
		return s;
	}
}
using Tr::gmax;
using Tr::gmin;
int main()
{
	cin>>n>>q;
	scanf("\n%s",ch+1);
	QS::make();
	fo(i,1,n) scanf("%d",&a[i]);
	
	fo(i,1,n)
	{
		while(st[0]&&a[i]>a[st[st[0]]]) rf[st[st[0]]]=i-1,st[st[0]--]=0;
		st[++st[0]]=i;
	}
	while(st[0]) rf[st[st[0]]]=n,st[st[0]--]=0;
	
	fod(i,n,1)
	{
		while(st[0]&&a[i]>a[st[st[0]]]) lf[st[st[0]]]=i+1,st[st[0]--]=0;
		st[++st[0]]=i;
	}	
	while(st[0]) lf[st[st[0]]]=1,st[st[0]--]=0;
	
	fo(i,1,n) aw[i]=(px){lf[i],rf[i],get(lf[i],rf[i]),a[i]};
	sort(aw+1,aw+n+1);
	
	fo(i,1,q)
	{
		scanf("%d%d%d",&ask[i][0],&ask[i][1],&ask[i][2]);
		d[i]=i;
	}
	sort(d+1,d+q+1,cmp);
	int j=1;
	Tr::n1=1;
	Tr::build(1,1,n);
	
	fo(i,1,q)
	{
		while(j<=n&&aw[j].v>=ask[d[i]][2])
		{
			Tr::ins(1,1,n,aw[j].r,aw[j].c);
			j++;
		}
		int l=ask[d[i]][0],r=ask[d[i]][1]+1;
		while(l+1<r)
		{
			int mid=(l+r)>>1;
			if(get(ask[d[i]][0],mid)>=ask[d[i]][2]) r=mid;
			else l=mid;
		}
		int wp;
		if(get(ask[d[i]][0],l)>=ask[d[i]][2]) wp=l;
		else wp=r;
		if(wp==ask[d[i]][1]+1) {ans[d[i]]=-1;continue;}
		ans[d[i]]=min(gmax(ask[d[i]][0],wp),gmin(wp,ask[d[i]][1]));
		
		l=ask[d[i]][0],r=ask[d[i]][1];
		while(l+1<r)
		{
			int mid=(l+r)>>1;
			if(get(mid,ask[d[i]][1])>=ask[d[i]][2]) l=mid;
			else r=mid;
		}
		if(get(r,ask[d[i]][1])>=ask[d[i]][2]) wp=r;
		else wp=l;
		ans[d[i]]=min(ans[d[i]],gmax(wp,ask[d[i]][1]));
	}
	fo(i,1,q) printf("%d\n",ans[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章