• luogu P4482 [BJWC2018]Border 的四种求法


    luogu

    对于每个询问从大到小枚举长度,哈希判断是否合法,AC

    假的(指数据)

    考虑发掘border的限制条件,如果一个border的前缀部分的末尾位置位置(x(lle x < r))满足(s[l,x]=s[r-(x-l+1)+1,r]),那么要满足最长公共后缀(lcs(x,r)ge x-l+1)

    (lcs(x,y))(x,y)两个点在串(S)的sam的(parent)树上的lca的(length),所以先把(S)的sam建出来,那么询问((l,r))就是对于(r)的对应点(u),最大的(xin [l,r))满足(x)对应的点(v)使得(length_{lca(u,v)}ge x-l+1).这个可以看成(u)点子树内的信息和到根路径上每个点的儿子子树信息

    先考虑子树信息,可以对每个点搞个线段树出来维护子树内的后缀对应点,每次在线段树上二分找最大的满足条件的(x)

    然后是后者.考虑把一个点子树信息全部挂在点上(就算是问到前面问到过的信息也没事,因为前面算的信息在更深的地方计算一定更优),然后就是一个链上线段树信息的询问.考虑树链剖分,每次询问(log)条重链上线段树的信息.注意到询问的部分都是若干条重链的前缀,所以可以dfs,维护出每个点到重链顶端的线段树信息,这里的话要使用可持久化线段树

    当然这样还不太优秀.我们可以把每个询问依次挂到从那个点跳到根时每条轻边的顶端,这样在dfs时只要维护一个重链的线段就好了.注意每个点到根会经过(log)条重链,所以轻链信息也可以挂到到根路径上每条轻边的顶端,每次把信息往线段树里插就行.还有如果处理完一个子树后,如果它到父亲的边是轻边,要清空他的线段树节点

    #include<bits/stdc++.h>
    #define LL long long
    #define uLL unsigned long long
    #define db double
    
    using namespace std;
    const int N=2e5+10;
    int rd()
    {
    	int x=0,w=1;char ch=0;
    	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    	return x*w;
    }
    int n,q,qr[N][2],an[N];
    char cc[N];
    int s[N*50],tg[N*50],ch[N*50][2],rt[N<<1],tot=1;
    void inst(int o1,int o2,int x,int y)
    {
    	int l=1,r=n;
    	s[o1]=min(s[o2],y);
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(x<=mid)
    		{
    			ch[o1][0]=++tot,ch[o1][1]=ch[o2][1];
    			o1=ch[o1][0],o2=ch[o2][0];
    			r=mid;
    		}
    		else
    		{
    			ch[o1][0]=ch[o2][0],ch[o1][1]=++tot;
    			o1=ch[o1][1],o2=ch[o2][1];
    			l=mid+1;
    		}
    		s[o1]=min(s[o2],y);
    	}
    }
    int merge(int o1,int o2)
    {
    	if(!o1||!o2) return o1+o2;
    	s[o1]=min(s[o1],s[o2]);
    	ch[o1][0]=merge(ch[o1][0],ch[o2][0]);
    	ch[o1][1]=merge(ch[o1][1],ch[o2][1]);
    	return o1;
    }
    int quer(int o,int l,int r,int ll,int rr,int x)
    {
    	if(!o) return 0;
    	if(l==r) return s[o]<=x?l:0;
    	int mid=(l+r)>>1;
    	if(ll<=l&&r<=rr)
    	{
    		if(ch[o][1]&&s[ch[o][1]]<=x) return quer(ch[o][1],mid+1,r,ll,rr,x);
    		return quer(ch[o][0],l,mid,ll,rr,x);
    	}
    	int an=0;
    	if(rr>mid) an=quer(ch[o][1],mid+1,r,ll,rr,x);
    	if(an) return an;
    	return quer(ch[o][0],l,mid,ll,rr,x);
    }
    vector<int> qs[N<<1],e[N<<1],op[N<<1];
    int fa[N<<1],len[N<<1],to[N<<1][26],sz[N<<1],la=1,tt=1,ps[N];
    int ss[N<<1],hs[N<<1],top[N<<1],px[N<<1];
    void extd(int cx,int ii)
    {
    	int np=++tt,p=la;
    	len[np]=len[p]+1,sz[np]=1,ps[ii]=np,px[np]=ii,la=tt;
    	while(!to[p][cx]) to[p][cx]=np,p=fa[p];
    	if(!p) fa[np]=1;
    	else
    	{
    		int q=to[p][cx];
    		if(len[q]==len[p]+1) fa[np]=q;
    		else
    		{
    			int nq=++tt;
    			fa[nq]=fa[q],len[nq]=len[p]+1;
    			memcpy(to[nq],to[q],sizeof(int)*26);
    			fa[np]=fa[q]=nq;
    			while(to[p][cx]==q) to[p][cx]=nq,p=fa[p];
    		}
    	}
    }
    void dfs1(int x)
    {
    	ss[x]=1;
    	vector<int>::iterator it;
    	for(it=e[x].begin();it!=e[x].end();++it)
    	{
    		int y=*it;
    		dfs1(y),sz[x]+=sz[y],ss[x]+=ss[y];
    		hs[x]=sz[hs[x]]>sz[y]?hs[x]:y;
    	}
    }
    void dfs2(int x,int ntp)
    {
    	top[x]=ntp;
    	if(hs[x]) dfs2(hs[x],ntp);
    	vector<int>::iterator it;
    	for(it=e[x].begin();it!=e[x].end();++it)
    	{
    		int y=*it;
    		if(y==hs[x]) continue;
    		dfs2(y,y);
    	}
    }
    void inii()
    {
    	for(int i=2;i<=tt;++i) e[fa[i]].push_back(i);
    	dfs1(1),dfs2(1,1);
    }
    void mark(int ii,int x)
    {
    	while(x)
    	{
    		qs[x].push_back(ii);
    		x=fa[top[x]];
    	}
    }
    void wk1(int x)
    {
    	if(px[x]) inst(rt[x]=++tot,0,px[x],px[x]);
    	vector<int>::iterator it;
    	for(it=e[x].begin();it!=e[x].end();++it)
    	{
    		int y=*it;
    		wk1(y);
    		rt[x]=merge(rt[x],rt[y]);
    	}
    	for(it=qs[x].begin();it!=qs[x].end();++it)
    	{
    		int i=*it,l=qr[i][0],r=qr[i][1];
    		an[i]=max(an[i],quer(rt[x],1,n,l,r-1,len[x]+l-1));
    	}
    }
    void wk2(int x)
    {
    	int latp=tot;
    	vector<int>::iterator it;
    	for(it=e[x].begin();it!=e[x].end();++it)
    	{
    		int y=*it;
    		if(y==hs[x]) continue;
    		wk2(y);
    	}
    	for(it=op[x].begin();it!=op[x].end();++it)
    	{
    		int i=*it,las=rt[x];
    		inst(rt[x]=++tot,las,i,i-len[x]);
    	}
    	for(it=qs[x].begin();it!=qs[x].end();++it)
    	{
    		int i=*it,l=qr[i][0],r=qr[i][1];
    		an[i]=max(an[i],quer(rt[x],1,n,l,r-1,l-1));
    	}
    	if(hs[x]) rt[hs[x]]=rt[x],wk2(hs[x]);
    	if(x==top[x])
    	{
    		rt[x]=0;
    		while(tot>latp)
    		{
    			s[tot]=ch[tot][0]=ch[tot][1]=0;
    			--tot;
    		}
    	}
    }
    
    int main()
    {
        //ntt
    	scanf("%s",cc+1);
    	n=strlen(cc+1);
    	for(int i=1;i<=n;++i) extd(cc[i]-'a',i);
    	inii();
    	q=rd();
    	for(int i=1;i<=q;++i)
    	{
    		qr[i][0]=rd(),qr[i][1]=rd();
    		an[i]=qr[i][0]-1;
    		mark(i,ps[qr[i][1]]);
    	}
    	s[0]=1<<30;
    	wk1(1);
    	while(tot)
    	{
    		s[tot]=ch[tot][0]=ch[tot][1]=0;
    		--tot;
    	}
    	for(int i=1;i<=n;++i)
    	{
    		int x=ps[i];
    		while(x)
    		{
    			op[x].push_back(i);
    			x=fa[top[x]];
    		}
    	}
    	for(int i=1;i<=tt;++i) rt[i]=0;
    	wk2(1);
    	for(int i=1;i<=q;++i) printf("%d
    ",an[i]-qr[i][0]+1);
    	return 0; 
    }
    
  • 相关阅读:
    js获取url参数方法
    JQuery ajax 传递数组
    删除数组中的重复元素
    jquery load的使用
    css3 圆角
    一个项目最忌讳什么
    Lambda 表达式笔记
    KE ASP.NET取到kindeditor 编辑器数据
    ADO.NET
    .NET中Bind和Eval的区别
  • 原文地址:https://www.cnblogs.com/smyjr/p/11520595.html
Copyright © 2020-2023  润新知