UOJ Logo zhouzixuan的博客

博客

bzoj 4453: cys就是要拿英魂!

2016-04-03 11:01:49 By zhouzixuan
【题目描述】:
    pps又开始dota视频直播了!
    一群每天被pps虐的蒟蒻决定学习pps的操作技术,他们把pps在这局放的技能记录了下来,每个技能用一个字符表示。经过研究,蒟蒻们发现字典序更大的连招威力更大。于是所有蒟蒻都想学习pps最强的连招。但是他们太弱了,不能学会整个视频里的连招,只能学会陈老师一段区间间内的连招,可是这个他们求不出,于是只好向你求助。为了蒟蒻们不再被pps虐(怎么可能),请你帮帮他们。
简化题意:
    给你一个字符串,每次询问你一段区间的字典序最大的子串。
【题目解法】:
    考虑离线处理
    设右端点为r,然后用set维护[l,r] (l<=r)的答案
    容易发现[l,r]的答案随着l的增加单调不减
    因此在set里面lower_bound一下就好了
    性质+:在set里面,任意两个位置i<j满足s[i,r]>s[j,r]
    考虑r->r+1是set的变化
    考虑两个后缀有s[i,n]>s[j,n] (i<j)
    显然有s[i,k]>s[j,k] (k>=j) 因此j不可能成为答案
    因此考虑那些s[i,n]<s[j,n] (i<j)的后缀
    设他们的lcp=L,显然:
    1.在(j<=k<j+L)的时候有 s[i,k]>s[j,k]
    2.在(k>=j+L)的时候有 s[i,k]<s[j,k]
    根据性质可知当r=j+L的时候需要把i删去
    我们成这样的情况为i伴随j(题解里面是这么说的)
    同时我们需要递归的把伴随i的,伴随伴随i的,...全部递归删去
    考虑证明:
    假设k伴随i,i伴随j
    1.k已经被删去,那么就不用管了
    2.k未被删去,显然有s[k,n]<s[i,n]<s[j,n]即k伴随j
      由于k未被删去有:k+lcp(s[k,n],s[i,n])>j+lcp(s[i,n],s[j,n])
      因此k与j的lcp就是lcp(s[i,n],s[j,n]),因此当r=j+lcp(s[i,n],s[j,n]) k要被删去
    得证
    最后考虑对于每一个点我们只需要找到最早被删去的时刻
    对后缀维护一个单调栈,每次加入后缀s[r+1,n]的时候和栈尾元素比较即可
    每个点只会被删除一次,插入一次
    复杂度O(N log N)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
const int N=200005;
struct node {int l,r,id;} a[N];
int n,m,ans[N],stack[N]; char s[N]; set<int> S;
bool v[N]; vector<int> del[N],b[N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline bool cmp(const node &a,const node &b) {return a.r<b.r;}
void init()
{
    scanf("%s",s+1); n=strlen(s+1); m=getint();
    for (int i=1; i<=m; i++) a[i].l=getint(),a[i].r=getint();
    for (int i=1; i<=m; i++) a[i].id=i; sort(a+1,a+m+1,cmp);
}
namespace Suffix_Array
{
    int rank[N],sa[N],x[N],y[N],m,c[N],tot,f[21][N];
    void init()
    {
        for (int i=1; i<=n; i++) c[x[i]=s[i]]++;
        for (int i=1; i<=n; i++) m=max(m,x[i]);
        for (int i=1; i<=m; i++) c[i]+=c[i-1];
        for (int i=1; i<=n; i++) sa[c[x[i]]--]=i;
    }
    void build()
    {
        for (int k=1; k<n; k*=2)
        {
            tot=0; for (int i=n-k+1; i<=n; i++) y[++tot]=i;
            for (int i=1; i<=n; i++) if (sa[i]>k) y[++tot]=sa[i]-k;
            for (int i=0; i<=m; i++) c[i]=0;
            for (int i=1; i<=n; i++) c[x[i]]++;
            for (int i=1; i<=m; i++) c[i]+=c[i-1];
            for (int i=n; i>=1; i--) sa[c[x[y[i]]]--]=y[i]; m=0;
            for (int i=1; i<=n; i++)
            {
                int u=sa[i],v=sa[i-1];
                if (i==1) {y[u]=++m; continue;}
                if ((x[u]!=x[v])||(x[u+k]!=x[v+k])) ++m;
                y[u]=m;
            }
            if (m>=n) break;
            for (int i=1; i<=n; i++) x[i]=y[i];
        }
        for (int i=1; i<=n; i++) rank[sa[i]]=i;
    }
    void solve()
    {
        int h=0;
        for (int i=1; i<=n; i++)
        {
            if (rank[i]==1) continue; int j=sa[rank[i]-1];
            for (;(j+h<=n)&&(i+h<=n)&&(s[i+h]==s[j+h]);h++);
            f[0][rank[i]]=h; if (h>0) h--;
        }
        for (int i=1; i<=20; i++)
            for (int j=1; j<=n; j++)
            {
                if (j+(1<<i)-1>n) break;
                f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
            }
    }
    inline int ask(int l,int r)
    {
        l=rank[l]; r=rank[r]; if (l>r) swap(l,r);
        l++; int k=(int)(log(r-l+1)/log(2));
        return min(f[k][l],f[k][r-(1<<k)+1]);
    }
}
void dfs(int x)
{
    if (v[x]) return; v[x]=true; S.erase(x);
    int size=b[x].size();
    for (int i=0; i<size; i++) dfs(b[x][i]);
}
void solve()
{
    int top=0;
    for (int i=1; i<=n; i++) del[i].clear(),b[i].clear();
    for (int i=1,now=1; i<=n; i++)
    {
        for (;top;top--)
        {
            int now=stack[top],len=Suffix_Array::ask(now,i);
            if (s[now+len]>s[i+len]) break;
            del[i+len].push_back(now); b[i].push_back(now);
        }
        stack[++top]=i; S.insert(i); int size=del[i].size();
        for (int j=0; j<size; j++) dfs(del[i][j]);
        for (;(now<=m)&&(a[now].r==i);now++) ans[a[now].id]=(*S.lower_bound(a[now].l));
    }
    for (int i=1; i<=m; i++) printf("%d\n",ans[i]);
}
int main()
{
    init();
    Suffix_Array::init();
    Suffix_Array::build();
    Suffix_Array::solve();
    solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。