zoukankan      html  css  js  c++  java
  • P6640 [BJOI2020] 封印(后缀自动机+线段树+二分)

    首先对每个位置处理出它作为右端点的最长子序列zz[i]

    这个问题是老题

    然后,每次区间询问就是求

    max(i-max(l,i-zz[i]+1)+1)

    这里注意到,i-zz[i]+1这玩意好像是单调不减的?

    尝试证一下...

    那么可以二分区间内第一个i-zz[i]+1大于l的位置p

    p前面半段的答案稳定

    p后面半段的答案用线段树维护zz[i]的最大值即可

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6;
    int len[maxn],link[maxn],nxt[maxn][26];
    int sz[maxn];
    int tot=1,lst=1;
    string s,t;
    int n;
    void sam_extend (char c) {
    	int cur=++tot;
    	len[cur]=len[lst]+1;
    	sz[cur]=1;
    	int p=lst;
    	while (p&&!nxt[p][c-'a']) {
    		nxt[p][c-'a']=cur;
    		p=link[p];
    	}
    	if (!p) {
    		link[cur]=1;
    	}
    	else {
    		int q=nxt[p][c-'a'];
    		if (len[p]+1==len[q]) {
    			link[cur]=q;
    		}
    		else {
    			int clone=++tot;
    			len[clone]=len[p]+1;
    			for (int i=0;i<26;i++) {
    				nxt[clone][i]=nxt[q][i];
    			}
    			link[clone]=link[q];
    			while (p&&nxt[p][c-'a']==q) {
    				nxt[p][c-'a']=clone;
    				p=link[p];
    			}
    			link[q]=link[cur]=clone;
    		}
    	}
    	lst=cur;
    }
    vector<int> g[maxn];
    int z[maxn];
    int zz[maxn];
    int c[maxn];
    void build (int i,int l,int r) {
    	if (l==r) {
    		c[i]=z[l];
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    	c[i]=max(c[i<<1],c[i<<1|1]);
    }
    int query (int i,int l,int r,int L,int R) {
    	if (l>=L&&r<=R) return c[i];
    	int mid=(l+r)>>1;
    	int ans=0;
    	if (L<=mid) ans=max(ans,query(i<<1,l,mid,L,R));
    	if (R>mid) ans=max(ans,query(i<<1|1,mid+1,r,L,R));
    	return ans;
    } 
    int main () {
    	cin>>s>>t;
    	n=s.size();
    	for (char i:t) {
    		sam_extend(i);
    	} 
    	int v=1,l=0;
    	for (int i=0;i<s.size();i++) {
    		while (v&&!nxt[v][s[i]-'a']) {
    			v=link[v];
    			l=len[v];
    		} 
    		if (nxt[v][s[i]-'a']) {
    			v=nxt[v][s[i]-'a'];
    			l++;
    		}
    		z[i+1]=l;
    		zz[i+1]=i+1-l+1;
    	}
    	//for (int i=1;i<=n;i++) printf("%d ",z[i]);
    	build(1,1,n);
    	int q;
    	cin>>q;
    	while (q--) {
    		int l,r;
    		scanf("%d%d",&l,&r);
    		int L=l,R=r,p=-1;
    		while (L<=R) {
    			int mid=(L+R)>>1;
    			if (zz[mid]>l) {
    				p=mid;
    				R=mid-1;
    			}
    			else {
    				L=mid+1;
    			}
    		}
    		int ans;
    		//printf("%d ",p);
    		if (p!=-1)
    			ans=max(p-l,query(1,1,n,p,r));
    		else
    			ans=r-l+1;
    		printf("%d
    ",ans);
    	}
    }
  • 相关阅读:
    Windows Server 2008 下解析二级域名的方法
    浅谈2D游戏设计模式--游戏剧情设计(1)
    公司做项目的几点经验总结(个人经验)
    ASP.NET 实现站内信功能(点对点发送,管理员群发)
    FusionChart 水印破解方法(代码版)
    如何使用SOIL在VS2012的 C++环境下显示图片
    C语言实现控制台中光标随意移动
    ASP.NET CMS模板培训教程
    如何用easyui+JAVA 实现动态拼凑datagrid表格(续)
    用JAVA实现插值查询的方法(算近似值,区间求法)
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15017603.html
Copyright © 2011-2022 走看看