1. 程式人生 > 其它 >THOJ-序列【字尾樹,Splay】

THOJ-序列【字尾樹,Splay】

正題

題目連結:http://xsy.gdgzez.com.cn/JudgeOnline/problem.php?cid=1600&pid=0


題目大意

給出一個長度為\(n\)的序列\(a\)\(q\)詢問給出\(k,d\)表示對於序列\(b_i=(a_i+d)\bmod m\)求字典序第\(k\)小的字尾。

\(1\leq n\leq 10^5,1\leq q\leq 5\times 10^5,1\leq m\leq 10^9\)


解題思路

顯然地我們需要離線然後按照\(d\)從小到大做,這樣我們就可以用個指標每次把最大的字元移到最前面去了。

我們先建立出一棵字尾樹,那麼排名就算\(dfs\)序,然後記下條邊最頂部的字元,當我們把一個字元提到最前面的時候我們就可以改變所有包括這個字元的路徑在它兄弟中的排名。

這樣每條邊最多修改一次,用\(Splay\)維護\(dfs\)序即可。

時間複雜度:\(O((n+q)\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map> 
using namespace std;
const int N=2e5+10;
struct node{
	int k,d,id;
}q[N*3];
int n,m,k,cnt,last,root,a[N],ans[N*3];
int fa[N],len[N],loc[N],pos[N],p[N],siz[N];
map<int,int> ch[N];
vector<int> G[N];
void Insert(int c){
	int p=last,np=last=++cnt;
	len[np]=len[p]+1;
	for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
	if(!p)fa[np]=1;
	else{
		int q=ch[p][c];
		if(len[p]+1==len[q])fa[np]=q;
		else{
			int nq=++cnt;len[nq]=len[p]+1;
			ch[nq]=ch[q];loc[nq]=loc[q];
			fa[nq]=fa[q];fa[np]=fa[q]=nq;
			for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
		}
	}
	return;
}
bool cmp(int x,int y)
{return pos[x]>pos[y];}
bool cMp(node x,node y)
{return x.d<y.d;}
bool CmP(int x,int y)
{return pos[x]<pos[y];}
struct SplayTree{
	int t[N][2],fa[N],siz[N],w[N],v[N]; 
	bool Direct(int x)
	{return t[fa[x]][1]==x;}
	void PushUp(int x){
		siz[x]=siz[t[x][0]]+siz[t[x][1]]+1;
		w[x]=w[t[x][0]]+w[t[x][1]]+(v[x]!=0);
		return;
	}
	void Rotate(int x){
		int y=fa[x],z=fa[y];
		int xs=Direct(x),ys=Direct(y);
		int w=t[x][xs^1];
		if(z)t[z][ys]=x;
		t[x][xs^1]=y;t[y][xs]=w;
		if(w)fa[w]=y;fa[y]=x;fa[x]=z;
		PushUp(y);PushUp(x);return;
	}
	void Splay(int x,int f){
		while(fa[x]!=f){
			int y=fa[x];
			if(fa[y]==f)Rotate(x);
			else if(Direct(x)==Direct(y))
				Rotate(y),
				Rotate(x);
			else Rotate(x),Rotate(x);
		}
		if(!f)root=x;
		return;
	}
	int Ask(int x,int k){
		if(k<=siz[t[x][0]])return Ask(t[x][0],k);
		if(k==siz[t[x][0]]+1)return x;
		return Ask(t[x][1],k-siz[t[x][0]]-1);
	}
	int Query(int x,int k){
		if(k<=w[t[x][0]])return Query(t[x][0],k);
		if(k==w[t[x][0]]+1&&v[x])return x;
		return Query(t[x][1],k-w[t[x][0]]-(v[x]!=0));
	}
	int Quant(int x)
	{Splay(x,0);
	return siz[t[x][0]]+1;}
}T;
void dfs(int x){
	T.t[last][1]=x;T.fa[x]=last;
	last=x;siz[x]=1;
	sort(G[x].begin(),G[x].end(),CmP);
	for(int i=0;i<G[x].size();i++)
		dfs(G[x][i]),siz[x]+=siz[G[x][i]];
	return;
}
void Updata(int x)
{if(!x)return;T.PushUp(x);Updata(T.fa[x]);return;}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	reverse(a+1,a+1+n);
	last=cnt=1;
	for(int i=1;i<=n;i++){
		Insert(a[i]);
		loc[last]=i;T.v[last]=i;
	}
	for(int i=1;i<=cnt;i++){
		G[fa[i]].push_back(i),p[i]=i;
		pos[i]=a[loc[i]-len[fa[i]]];
	}
	last=cnt+1;dfs(1);
	sort(p+2,p+1+cnt,cmp);
	T.t[last][1]=cnt+2;T.fa[cnt+2]=last;
	Updata(cnt+2);root=cnt+1;
	for(int i=1;i<=k;i++)
		scanf("%d%d",&q[i].d,&q[i].k),q[i].id=i;
	sort(q+1,q+1+k,cMp);
	for(int i=1,z=2;i<=k;i++){
		while(z<=cnt&&pos[p[z]]+q[i].d>=m){
			int x=p[z];
			int l=T.Quant(x),r=l+siz[x]-1; 
			int y=T.Quant(fa[x]);
			l=T.Ask(root,l-1);r=T.Ask(root,r+1);
			T.Splay(l,0);T.Splay(r,l);
			int w=T.t[r][0];T.t[r][0]=0;
			T.PushUp(r);T.PushUp(l);
			l=T.Ask(root,y);r=T.Ask(root,y+1);
			T.Splay(l,0);T.Splay(r,l);
			T.fa[w]=r;T.t[r][0]=w;
			T.PushUp(r);T.PushUp(l);
			z++;
		}
		int x=T.Query(root,q[i].k);
		T.Splay(x,0);
		ans[q[i].id]=n-T.v[x]+1;
	}
	for(int i=1;i<=k;i++)
		printf("%d\n",ans[i]);
	return 0;
}