码迷,mamicode.com
首页 > 其他好文 > 详细

LOJ #6041. 事情的相似度

时间:2018-04-21 19:43:13      阅读:330      评论:0      收藏:0      [点我收藏+]

标签:parent   back   离线   getc   else   有一个   就是   覆盖   struct   

Description

人的一生不仅要靠自我奋斗,还要考虑到历史的行程。
历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。
你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。
你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。
两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。
现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?

Solution

考虑暴力做法,离线询问
因为两个串的最长公共后缀,就是所代表节点的 \(lca\)\(len\)
每一次加入一个前缀,在 \(parent\) 树上往上跳,如果一个点被跳过我们就更新答案
因为右端点固定时,左端点越大,对询问的贡献肯定越多,所以直接覆盖掉这个节点事件的下标(也就是 \(pos\)),所以我们维护这个节点子树内的最大 \(pos\) 值就行了
但是还有一个左端点限制,我们开一个左端点为下标的树状数组维护一下就好了
实际上这个过程就是 \(LCT\)\(access\),那么用 \(LCT\) 做这个过程复杂度就可以均摊为 \(access\) 的复杂度了

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
template<class T>void gi(T &x){
    int f;char c;
    for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for(x=0;c>='0'&&c<='9';c=getchar())x=x*10+(c&15);x*=f;
}
int fa[N],ch[N][2],len[N],cur=1,cnt=1,n,Q,pos[N];
char s[N];int tr[N],ans[N];
struct data{int x,id;};
vector<data>v[N];vector<data>::iterator it;
inline void add(int x,int y){for(int i=x;i>=1;i-=(i&(-i)))tr[i]=max(tr[i],y);}
inline int qry(int x){
    int ret=0;
    for(int i=x;i<=n;i+=(i&(-i)))ret=max(ret,tr[i]);
    return ret;
}
namespace lct{
    int fa[N],ch[N][2],w[N],la[N];
    inline void mark(int x,int y){w[x]=y;la[x]=y;}
    inline bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    inline void rotate(int x){
        int y=fa[x];bool t=ch[y][1]==x;
        ch[y][t]=ch[x][!t];fa[ch[y][t]]=y;
        ch[x][!t]=y;fa[x]=fa[y];
        if(!isrt(y))ch[fa[y]][ch[fa[y]][1]==y]=x;
        fa[y]=x;
    }
    inline void pushdown(int x){
        if(!la[x])return ;
        mark(ch[x][0],la[x]);mark(ch[x][1],la[x]);la[x]=0;
    }
    inline void Push(int x){if(!isrt(x))Push(fa[x]);pushdown(x);}
    inline void splay(int x){
        Push(x);
        while(!isrt(x)){
            int y=fa[x],p=fa[y];
            if(isrt(y))rotate(x);
            else if((ch[p][0]==y)==(ch[y][0]==x))rotate(y),rotate(x);
            else rotate(x),rotate(x);
        }
    }
    inline void access(int x,int id){
        int y=0;
        while(x)splay(x),ch[x][1]=y,add(w[x],len[x]),x=fa[y=x];
        mark(y,id);
    }
}
inline void ins(int c){
    int p=cur;cur=++cnt;len[cur]=len[p]+1;
    for(;p && !ch[p][c];p=fa[p])ch[p][c]=cur;
    if(!p)fa[cur]=1;
    else{
        int q=ch[p][c];
        if(len[p]+1==len[q])fa[cur]=q;
        else{
            int nt=++cnt;len[nt]=len[p]+1;
            memcpy(ch[nt],ch[q],sizeof(ch[q]));
            fa[nt]=fa[q];fa[cur]=fa[q]=nt;
            for(;p && ch[p][c]==q;p=fa[p])ch[p][c]=nt;
        }
    }
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  int x,y;
  cin>>n>>Q;
  scanf("%s",s+1);
  for(int i=1;i<=n;i++)ins(s[i]-'0'),pos[i]=cur;
  for(int i=1;i<=Q;i++)gi(x),gi(y),v[y].push_back((data){x,i});
  for(int i=2;i<=cnt;i++)lct::fa[i]=fa[i];
  for(int i=1;i<=n;i++){
      lct::access(pos[i],i);
      for(it=v[i].begin();it!=v[i].end();++it)ans[it->id]=qry(it->x);
  }
  for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
  return 0;
}

LOJ #6041. 事情的相似度

标签:parent   back   离线   getc   else   有一个   就是   覆盖   struct   

原文地址:https://www.cnblogs.com/Yuzao/p/8902237.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!