zoukankan      html  css  js  c++  java
  • 题解[P7361拜神]

    原题链接

    题意:给定一个字符串,每次询问一段区间中出现两次的最长子串长度。

    思路还较清新,先将询问按右端点 (r) 排序,看对每个询问 (l) 能怎么方便求出。

    将每个 ([1,r]) 内的子串 (p) ,其在 ([1,r])次后出现的位置的右端点与左端点看做一条线段 ([s_p,t_p])

    那对每个 (l) 只需查出 (mathrm{Max}left{left(mathop{mathrm{Max}}limits_{s_pleq lleq t_p}t_p-l+1 ight),left(mathop{mathrm{Max}}limits_{s_pgeq l} t_p-s_p+1 ight) ight})

    这相当于是找出覆盖 (l) 的线段中的右端点最大值 (-l) ,与及不覆盖 (l) 且在 (l) 右侧的线段长度最大值的最大值。

    由于对于 ( ext{endpos}) 相同的子串右端点集合相同,且上式需要的线段是越长越好。

    于是我们需要的线段仅为 ( ext{endpos}) 相同的串中最长的一个,先想如何更新次右端点。

    联系一下这题,不难得出 ( ext{LCT}) 维护的方法。

    就是建出 ( ext{parent tree}) 后记录每个等价类出现位置的最右位置,以及次右位置。

    (r) 向右移时单次 (access) 更改为了求上式需维护的信息,同时打上将次右值变成最右值,最右值更新为 (r) 的标记。

    剩下的就是考虑上式怎么求出。

    首先第二个 (mathrm{Max}) 比较好做,因为每个子串的 (s_p) 单调递增,可以直接将 ([1,s_p]) 的信息与 (t_p-s_p+1) 取最大值。

    对于第一个 (mathrm{Max}) 先考虑没有 (access) 顺带需要的修改该怎么做。

    这可以将每个在 ([s_p,t_p]) 之间的点,用 (t_p) 更新能覆盖这个点的线段最大右端点。

    观察到每次更改所影响到的线段可视为向右平移一段区间,事实上之前的区间取最大值并不会影响之后的查询。

    这是因为之后查询的 (l) 如果在原先 (t_p) 之后,就不会影响答案。

    而在原先 (t_p) 之前的 (l) 必定会查到右移之后的的整个字符串,或者之后的右端点比之前右端点靠右。这两种情况分别对应右移前后,是否与原先字符串重合。而这两种情况一定会在第一、二个 (mathrm{Max}) 中得出的长度比之前优,会将之前的标记覆盖掉。

    于是只需要一个支持区间取 (mathrm{Max}) 的数据结构,用线段树容易实现。

    最终时间复杂度是 (O(nlog^2n)) 的。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    int n,m,x,xx,y,tot=1,last=1,tmp,l,r;char ch;
    int rot_xx,rot_y,s_i,acs_y,a;bool rot_b;
    int mxlen[N],link[N],trans[N][26],f[N],res[N];
    int lst[N],lst_[N],son[N][2],anc[N],cov[N];
    inline void read(int &x){
    	x=0;ch=getchar();while(ch<48||ch>57)ch=getchar();
    	while(ch>47&&ch<58)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    }
    void write(int x){if(x>9)write(x/10);putchar(48+x%10);}
    #define ls k<<1
    #define rs k<<1|1
    struct segment_tree{
    	int tag[N<<2],res;//这里用了标记永久化实现区间取max,单点查
    	void update(int k,int l,int r,int x,int y,int v){
    		if(x<=l&&r<=y)tag[k]=max(tag[k],v);
    		else {
    			int mid=(l+r)>>1;
    			if(x<=mid)update(ls,l,mid,x,y,v);
    			if(mid<y)update(rs,mid+1,r,x,y,v);
    		}
    	}
    	void inquiry(int k,int l,int r,int pos){
    		res=max(res,tag[k]);
    		if(l^r){
    			int mid=(l+r)>>1;
    			if(pos<=mid)inquiry(ls,l,mid,pos);
    			else inquiry(rs,mid+1,r,pos);
    		}
    	}
    }S,S_;
    inline void extend(int a){
    	tmp=++tot;y=last;mxlen[tmp]=mxlen[y]+1;
    	for(;y&&!trans[y][a];y=link[y])trans[y][a]=tmp;
    	if(!y)link[tmp]=1;
    	else {
    		x=trans[y][a];
    		if(mxlen[x]==mxlen[y]+1)link[tmp]=x;
    		else {
    			xx=++tot;mxlen[xx]=mxlen[y]+1;link[xx]=link[x];
    			memcpy(trans[xx],trans[x],sizeof(trans[xx]));
    			for(;y&&trans[y][a]==x;y=link[y])trans[y][a]=xx;
    			link[tmp]=link[x]=xx;
    		}
    	}
    	last=tmp;
    }
    inline bool nroot(int x){return son[anc[x]][0]==x||son[anc[x]][1]==x;}
    inline bool p(int x){return son[anc[x]][1]==x;}
    inline void cov_(int x,int v){lst_[x]=lst[x];lst[x]=cov[x]=v;}
    //将次右变为最右,最右由覆盖标记更新
    inline void pushdown(int x){
    	if(cov[x]){
    		if(son[x][0])cov_(son[x][0],cov[x]);
    		if(son[x][1])cov_(son[x][1],cov[x]);
    		cov[x]=0;
    	}
    }
    void pushall(int x){if(nroot(x))pushall(anc[x]);pushdown(x);}
    inline void rotate(int x){
    	rot_y=anc[x];rot_xx=anc[rot_y];rot_b=p(x);
    	if(nroot(rot_y))son[rot_xx][p(rot_y)]=x;
    	anc[x]=rot_xx;
    	anc[son[rot_y][rot_b]=son[x][!rot_b]]=rot_y;
    	anc[son[x][!rot_b]=rot_y]=x;
    }
    inline void splay(int x){
    	pushall(x);
    	for(;s_i=anc[x],nroot(x);rotate(x))if(nroot(s_i))rotate(p(x)==p(s_i)?s_i:x);
    }
    inline void access(int x,int pos){
    	for(y=0;x;y=x,x=anc[x]){
    		splay(x);son[x][1]=y;
    		l=lst[x]-mxlen[x]+1;r=lst[x];//l,r 为这个整个子串将要移动到的区间
    		if(lst[x]&&x^1)S.update(1,1,n,l,r,r),S_.update(1,1,n,1,l,r-l+1);
    	}//这里access由于每次是之前跳父亲来的,能保证x一定是当前splay中mxlen最大的
    	cov_(y,pos);
    }
    int to[N],nextn[N],h[N],id[N],edg;
    inline void add(int x,int y,int i){to[++edg]=y,nextn[edg]=h[x],h[x]=edg;id[edg]=i;}
    main(){
    	read(n);read(m);while(ch<97)ch=getchar();
    	register int i,j;
    	for(i=1;i<=n;++i)a=ch-97,extend(a),f[i]=tmp,ch=getchar();
    	for(i=1;i<=tot;++i)anc[i]=link[i];
    	for(i=1;i<=m;++i)read(x),read(y),add(y,x,i);
    	for(i=1;i<=n;++i){
    		access(f[i],i);
    		for(j=h[i];j;j=nextn[j]){
    			S.res=S_.res=0;
    			S.inquiry(1,1,n,to[j]);
    			S_.inquiry(1,1,n,to[j]);
    			res[id[j]]=max(S.res-to[j]+1,S_.res);
    		}
    	}
    	for(i=1;i<=m;++i)write(res[i]),putchar('
    ');
    }
    
  • 相关阅读:
    go学习笔记day19
    Mac11系统 SIP保护 如何在根目录创建data文件夹
    vue首页加载文件过多,去掉预加载
    NPM更换国内源
    autoCAD2007 图层
    读《现代JavaScript》笔记①——Map and Set(映射和集合)
    pubsubjs发布订阅
    vuecli中配置less并使用全局变量
    React组件通讯
    v6版reactrouterdom的改变
  • 原文地址:https://www.cnblogs.com/Y-B-X/p/15430426.html
Copyright © 2011-2022 走看看