zoukankan      html  css  js  c++  java
  • [JZOJ6241]【NOI2019模拟2019.6.29】字符串【数据结构】【字符串】

    Description

    给出一个长为n的字符串(S)和一个长为n的序列(a)
    定义一个函数(f(l,r))表示子串(S[l..r])的任意两个后缀的最长公共前缀的最大值。

    现在有q组询问,每组询问给出(L,R,x)
    你需要找到一个子串(S[l,r])满足([l,r]subset[L,R])(f(l,r)geq x)
    同时需要满足(max(a[l..r]))最小

    求这个最小值,无解则输出-1
    (n,qleq 50000)

    Solution

    这道题实际上分成两部分(谴责拼题行为)

    第一部分快速计算f(l,r)
    建出SAM,用个set维护right集,在parent树上启发式合并,显然Right集合中我们只需要考虑哪些相邻的位置有用(不相邻显然不优),合并的时候把要插入的位置的前驱以及它们的LCP构成一个三元组,后继也一样处理,这样我们得到了一个可能更新答案的三元组序列((l,x,r)),表示(S[l..x]=S[r-(x-l),r])

    观察对答案的贡献,它能贡献的区间满足(R>r,l<=x),相当于区间取max以及区间对等差数列取max
    维护两棵线段树分别来弄,等差数列我们只需要维护合法右端点最大值即可,两个都是区间取max单点查询,可以用struct写在一起

    还需要保证右端点的限制,因此对所有二元组按右端点排序建主席树即可。
    区间修改的主席树似乎比较麻烦?我们发现这题不需要下传标记,因此直接先将当前位置的操作扔进去再继承前一个的。

    这样就能够在(O(nlog^2n))预处理(启发式合并),(O(log n))计算一个区间的f

    第二部分计算答案

    显然只有笛卡尔树上的n个极大区间是有用的,计算出它们的f

    对于查询区间([L,R]),我们只需要找到被它完全包含的极大区间,然后分别以左右端点开始二分另一个端点即可。

    找到包含的区间似乎是个二维问题?

    不需要
    对于左端点我们二分了一个位置,表示右端点在这左边的都不合法。

    其他的合法区间的右端点一定在这右边,

    此时我们不需要管左端点的限制了,因为超出去答案显然更劣。
    直接一维区间最大值即可

    这一部分也是(O(nlog^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("
    %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
    ",ans[i]);
    }
    
  • 相关阅读:
    并发与并行
    OpenCV 图像集合操作
    C++ 输出时间
    绘制模型图
    检测图像文件是否损坏
    QImage,Mat ,QByteArray转换
    图像拼接3
    图像拼接2】
    图像拼接 Stitcher
    《将博客搬至CSDN》
  • 原文地址:https://www.cnblogs.com/BAJimH/p/11111552.html
Copyright © 2011-2022 走看看