【LOJ 6041】「雅礼集训 2017 Day7」事情的相似度

Description

人的一生不仅要靠自我奋斗,还要考虑到历史的行程。

历史的行程可以抽象成一个 01 串,作为一个年纪比较大的人,你希望从历史的行程中获得一些姿势。

你发现在历史的不同时刻,不断的有相同的事情发生。比如,有两个人同时在世纪之交 11 年的时候上台,同样喜欢与洋人谈笑风生,同样提出了以「三」字开头的理论。

你发现,一件事情可以看成是这个 01 串的一个前缀,这个前缀最右边的位置就是这个事情的结束时间。

两件事情的相似度可以看成,这两个前缀的最长公共后缀长度。

现在你很好奇,在一段区间内结束的事情中最相似的两件事情的相似度是多少呢?

Input

第一行两个整数 n、m,表示串长和询问个数。
第二行长度为 n 的 01 串,表示历史的行程。
接下来 m 行,每行两个正整数 l r 表示询问的区间,包括端点,保证1≤l<r≤n

Output

输出 m 行,对每个询问输出一个整数表示最大的相似度。

 

建出原串的SAM,则两个前缀的最长公共后缀为他们在parent树上的lca,问题转化为求区间内前缀两两lca深度的最大值。

将询问离线,按右端点从小到大排序。我们考虑每次加入一个字母,就将他们在parent树上到根节点的路径打上他们的标记。往根节点跑的过程中,若遇到了以前打的标记,则该节点为旧标记与新标记的lca。贪心可得应把标记尽量覆盖为较大的值。用树状数组来统计答案,下标为左端点,每次查询下标大于等于该询问左端点的最大深度。向根跑的过程中每一次遇到旧标记,就在树状数组上更新答案,并给该节点打上新标记。

往根节点跑的过程实际上就是LCT的access。

 

  1 #include<cstdio>
  2 #include<algorithm> 
  3 #include<cstring>
  4 #include<vector>
  5 #define LL long long
  6 using namespace std;
  7 const int N=1e5+5;
  8 int n,m,r,last,size,root;
  9 int p[N],mx[N],ans[N],num[N];
 10 int c[N*2][2],fa[N*2],v[N*2],tag[N*2];
 11 char s[N];
 12 vector<int> q[N];
 13 struct sam{int mx,fa,ch[2];}t[N*2];
 14 int read()
 15 {
 16     int x=0,f=1;char c=getchar();
 17     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
 18     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
 19     return x*f;
 20 }
 21 int lowbit(int x){return x&(-x);}
 22 void modify(int x,int v){x=n-x+1;while(x<=n)mx[x]=max(mx[x],v),x+=lowbit(x);}
 23 int query(int x){x=n-x+1;int ans=0;while(x)ans=max(ans,mx[x]),x-=lowbit(x);return ans;}
 24 void ins(int c,int id)
 25 {
 26     int np=++size;num[id]=np;
 27     t[np].mx=t[last].mx+1;
 28     int x=last;last=np;
 29     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
 30     if(!x)t[np].fa=root;
 31     else
 32     {
 33         int y=t[x].ch[c];
 34         if(t[y].mx==t[x].mx+1)t[np].fa=y;
 35         else
 36         {
 37             int nq=++size;
 38             t[nq]=t[y];t[nq].mx=t[x].mx+1;
 39             t[y].fa=t[np].fa=nq;
 40             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
 41         }
 42     }
 43 }
 44 bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
 45 void change(int x,int val){v[x]=tag[x]=val;}
 46 void down(int x)
 47 {
 48     if(!tag[x])return;
 49     if(c[x][0])change(c[x][0],tag[x]);
 50     if(c[x][1])change(c[x][1],tag[x]);
 51     tag[x]=0;
 52 }
 53 void rotate(int x)
 54 {
 55     int y=fa[x],z=fa[y],l,r;
 56     if(c[y][0]==x)l=0;else l=1;r=l^1;
 57     if(!isroot(y)){if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;}
 58     fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
 59     c[y][l]=c[x][r];c[x][r]=y;
 60 }
 61 void relax(int x){if(!isroot(x))relax(fa[x]);down(x);}
 62 void splay(int x)
 63 {
 64     relax(x);
 65     while(!isroot(x))
 66     {
 67         int y=fa[x],z=fa[y];
 68         if(!isroot(y))
 69         {
 70             if((c[y][0]==x)^(c[z][0]==y))rotate(x);
 71             else rotate(y);
 72         }
 73         rotate(x);
 74     }
 75 }
 76 void access(int x,int val)
 77 {
 78     int o=0;
 79     while(x)
 80     {
 81         splay(x);modify(v[x],t[x].mx);
 82         c[x][1]=o;o=x;x=fa[x];
 83     }
 84     tag[o]=v[o]=val;
 85 }
 86 void build(){for(int i=1;i<=size;i++)fa[i]=t[i].fa;}
 87 int main()
 88 {
 89     n=read();m=read();
 90     scanf("%s",s+1);
 91     for(int i=1;i<=m;i++)
 92     {
 93         p[i]=read();r=read();
 94         q[r].push_back(i);
 95     }
 96     last=size=root=1;
 97     for(int i=1;i<=n;i++)ins(s[i]-'0',i);
 98     build();
 99     for(int i=1;i<=n;i++)
100     {
101         access(num[i],i);
102         int sz=q[i].size();
103         for(int j=0;j<sz;j++)
104         {
105             int x=q[i][j];
106             ans[x]=query(p[x]);
107         }
108     }
109     for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
110     return 0;
111 }
View Code

 

posted @ 2018-04-21 11:39  Zsnuo  阅读(855)  评论(0编辑  收藏  举报