后缀数组小结

       后缀数组是从去成都之前开始学的,但是当时只是写了遍板子既不会背也不会用。从成都回来之后依然不想做这个专题,到一月中旬才填坑。刚开始因为板子不理解非常费力气,后来慢慢写着写着就会背了,背着背着也有一种理解了一点的错觉……可能并不是十分理解原理,只不过学会了怎么用而已。按自己原来的题解和日记整理一下。

       入门时看的资料:五分钟并不能学会后缀数组用法总结罗穗骞的论文。板子是从Narcissus那学的,他的总结也十分详细,给了我很大帮助。

 

       1.板子与套路

 1 void rsort()
 2 {
 3     memset(tx,0,sizeof(int)*(m+1));
 4     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 5     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 6     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 7 }
 8 void suffix()
 9 {
10     int i,j,k,l,p;
11     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
12     m=inf,rsort();
13     for(l=1,p=1;p<n;m=p,l<<=1)
14     {
15         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
16         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
17         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
18         for(i=2;i<=n;i++)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
19     }
20     for(i=1,k=0;i<=n;h[rk[i++]]=k)
21         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
22 }

        $rsort$部分是一个基数排序。这是一个倍增的过程,先求出$sa$和$rk$,再根据性质快速求出$h$(即上列讲解中的$height$)。$sa[i]$的含义是排名为$i$的后缀开头的位置,$rk[i]$的含义是以$i$位置开头的后缀的排名,$h[i]$的含义是排名为$i$和$i-1$的后缀的$lcp$(最长公共前缀);$tx$和$ty$是用于实现基数排序的桶,$n$是字符串长度而$m$是字符集大小。最常见的问题是快速求两个后缀的$lcp$,答案是它们$rk$之间$h$的最小值。如果需要解决多串问题,常把多个串拼成一个并用分隔符隔断。比较裸的题有BZOJ1717BZOJ4698

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int sj=20010;
 7 int n,k,le,ri,mid,mx,pos,a[sj],rk[sj],sa[sj],m,cnt,tx[sj],ty[sj],h[sj];
 8 struct ls
 9 {
10     int num,vl;
11 }s[sj];
12 int comp(const ls&x,const ls&y)
13 {
14     return x.vl<y.vl;
15 }
16 bool check(int x)
17 {
18     for(int i=1;i<=n;i=pos+1)
19     {
20         pos=i;
21         while(h[pos]>=x)  pos++;
22         if(pos-i>=k-1)  return 1;
23         if(pos!=i)  pos--;
24     }
25     return 0;
26 }
27 void rsort()
28 {
29     memset(tx,0,sizeof(int)*(m+1));
30     for(int i=1;i<=n;++i)  tx[rk[ty[i]]]++;
31     for(int i=1;i<=m;++i)  tx[i]+=tx[i-1];
32     for(int i=n;i>=1;--i)  sa[tx[rk[ty[i]]]--]=ty[i];
33 }
34 void suffix()
35 {
36     int i,j,k,p,l;
37     for(int i=1;i<=n;++i)  rk[i]=a[i],ty[i]=i;
38     m=sj-10;rsort();
39     for(p=1,l=1;p<n;m=p,l<<=1)
40     {
41         for(p=0,i=n-l+1;i<=n;++i)  ty[++p]=i;
42         for(i=1;i<=n;++i)  if(sa[i]>l)  ty[++p]=sa[i]-l;
43         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
44         for(i=2;i<=n;++i)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
45     }
46     for(i=1,k=0;i<=n;h[rk[i++]]=k)  
47         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];++k);
48 }
49 int main()
50 {
51     scanf("%d%d",&n,&k);
52     for(int i=1;i<=n;i++)  scanf("%d",&a[i]),s[i].vl=a[i],s[i].num=i;
53     sort(s+1,s+n+1,comp);
54     a[s[1].num]=1,mx=1;
55     for(int i=2;i<=n;i++)
56     {
57         if(s[i-1].vl!=s[i].vl)  mx++;
58         a[s[i].num]=mx;
59     }
60     le=1,ri=n;
61     suffix();
62     while(le<ri)
63     {
64         mid=(le+ri+1)>>1;
65         if(check(mid))  le=mid;
66         else  ri=mid-1;
67     }
68     printf("%d",le);
69     return 0;
70 }
1717
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int sj=1000010;
 6 const int bd=2000;
 7 int n,m,h[sj],tx[sj],ty[sj],rk[sj],sa[sj],a[sj];
 8 int le,ri,mid,num,bl[sj],pos,la,nt,cnt;
 9 bool r[sj];
10 void rsort()
11 {
12     memset(tx,0,sizeof(int)*(m+1));
13     for(int i=1;i<=cnt;++i)  tx[rk[ty[i]]]++;
14     for(int i=1;i<=m;++i)    tx[i]+=tx[i-1];
15     for(int i=cnt;i>=1;--i)  sa[tx[rk[ty[i]]]--]=ty[i];
16 }
17 void getsa()
18 {
19     int i,j,p,l,k;
20     for(i=1;i<=cnt;++i)  rk[i]=a[i],ty[i]=i;
21     m=4000,rsort();
22     for(p=1,l=1;p<cnt;m=p,l<<=1)
23     {
24         for(p=0,i=cnt-l+1;i<=cnt;++i)  ty[++p]=i;
25         for(i=0;i<=cnt;++i)  if(sa[i]>l)  ty[++p]=sa[i]-l;
26         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
27         for(i=2;i<=cnt;++i)  
28             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
29     }
30     for(i=1,k=0;i<=cnt;h[rk[i++]]=k)
31         for(k=k?k-1:0,j=sa[rk[i]-1];a[i+k]==a[j+k];++k);
32 }
33 bool check(int x)
34 {
35     for(int i=1;i<=cnt;i=pos+1)
36     {
37         pos=i,num=0;
38         while(h[pos]>=x) 
39         {
40            if(bl[sa[pos]]&&!r[bl[sa[pos]]])  num++;
41            r[bl[sa[pos]]]=1;
42            pos++;
43         }
44         if(h[i]>=x)  
45             if(bl[sa[i-1]]&&!r[bl[sa[i-1]]])  num++;
46         for(int j=i;j<pos;++j)  r[bl[sa[j]]]=0;
47         if(num==n)  return 1;
48     }
49     return 0;
50 }
51 int main()
52 {
53     scanf("%d",&n);
54     ri=0x7fffffff;
55     for(int i=1;i<=n;++i)
56     {
57         scanf("%d",&m);
58         if(m<ri)  ri=m;
59         for(int j=1;j<=m;++j)
60         {
61             scanf("%d",&nt);
62             a[++cnt]=nt-la+bd,la=nt;
63             bl[cnt]=i;
64         }
65         ++cnt,la=0;
66     }
67     ri--;
68     getsa();
69     while(le<ri)
70     {
71         mid=(le+ri+1)>>1;
72         if(check(mid))  le=mid;
73         else  ri=mid-1;
74     }
75     if(n==1)  printf("0");
76     else      printf("%d",le+1);
77     return 0;
78 }
4698

 

       2.BZOJ3238 差异

      观察题目中给的式子,每个后缀的长度都会被加$n-1$次,后缀的长度又是一个公差为$1$的等差数列,可以直接用求和公式$n*(n+1)/2*(n-1)$完成统计。剩下的就是后缀之间两两$lcp$的长度和,可以转化为每个长度*$lcp$等于该长度的后缀数。$lcp$是$rk$区间$h$的最小值,可以用单调栈求出每个$h$控制的范围,就变成暑假集训常做的那种NOIP模拟题了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<stack>
 5 #define ll long long
 6 using namespace std;
 7 const int sj=500010;
 8 stack<int> q;
 9 int tx[sj],ty[sj],rk[sj],sa[sj],m,h[sj],a[sj],lm[sj],rm[sj];
10 char s[sj];
11 ll ans,n;
12 void rsort()
13 {
14     memset(tx,0,sizeof(int)*(m+1));
15     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
16     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
17     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
18 }
19 void suffix()
20 {
21     int i,j,k,l,p;
22     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
23     m=26;rsort();
24     for(l=1,p=1;p<n;m=p,l<<=1)
25     {
26         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
27         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
28         rsort(),swap(ty,rk),rk[sa[1]]=p=1;
29         for(i=2;i<=n;i++)
30             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
31     }
32     for(i=1,k=0;i<=n;h[rk[i++]]=k)
33         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
34 }
35 int main()
36 {
37     scanf("%s",s);
38     n=strlen(s);
39     ans=n*(n+1)/2*(n-1);
40     for(int i=1;i<=n;i++)  a[i]=s[i-1]-'a'+1;
41     suffix();
42     q.push(0),h[0]=-1;
43     for(int i=1;i<=n;i++)
44     {
45         while(h[q.top()]>=h[i])  q.pop();
46         lm[i]=q.top()+1;
47         q.push(i);
48     }
49     while(!q.empty())  q.pop();
50     q.push(n+1),h[n+1]=-1;
51     for(int i=n;i>=1;i--)
52     {
53         while(h[q.top()]>h[i])  q.pop();
54         rm[i]=q.top()-1;
55         q.push(i);
56     }
57     for(ll i=1;i<=n;i++)  
58         ans-=(i-lm[i]+1)*(rm[i]-i+1)*2*h[i];
59     printf("%lld",ans);
60     return 0;
61 }
3238

 

 

       3.BZOJ4556 字符串

        求一个字符串内一个子串和另一个子串的子串们的$lcp$。$lcp$的长度受到h和两个子串长度的双重限制,于是考虑二分答案来确定可行区间的端点。例如我们要求$[a,b]$的子串和$[c,d]$的$lcp$,二分$lcp$长度为$mid$,那么只能是开头在$[a,b-mid+1]$的后缀和$[c,d]$的$lcp$。维护一个下标为字符串中对应位置、权值为$rk$的线段树,每次$check$利用$h$数组二分两次求出$rk$在$[l,r]$的区间满足要求,再在主席树上查询$[a,b-mid+1]$中是否有rk落在$[l,r]$中。总复杂度$mlog^2n$。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 using namespace std;
  5 const int sj=100010;
  6 int rk[sj],tx[sj],ty[sj],sa[sj],h[sj],a[sj],p[sj][20];
  7 int rt[sj],n,q,a1,a2,a3,a4,l,r,mid,lm,rm,cnt,m;
  8 char s[sj];
  9 struct tree
 10 {
 11     int sum,lc,rc;
 12 }t[sj*50];
 13 inline int read()
 14 {
 15     int jg=0,jk=getchar()-'0';
 16     while(jk<0||jk>9)    jk=getchar()-'0';
 17     while(jk>=0&&jk<=9)  jg*=10,jg+=jk,jk=getchar()-'0';
 18     return jg;
 19 }
 20 inline int bj(int x,int y)
 21 {
 22     return x<y?x:y;
 23 }
 24 void pre()
 25 {
 26     for(int i=1;i<=n;i++)  p[i][0]=h[i];
 27     for(int j=1;(1<<j)<=n;j++)
 28         for(int i=1;i+(1<<j)-1<=n;i++)
 29             p[i][j]=bj(p[i][j-1],p[i+(1<<(j-1))][j-1]);
 30 }
 31 void rsort()
 32 {
 33     memset(tx,0,sizeof(int)*(m+1));
 34     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 35     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 36     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 37 }
 38 void suffix()
 39 {
 40     int i,j,k,p,l;
 41     for(int i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
 42     m=26;rsort(); 
 43     for(p=1,l=1;p<n;m=p,l<<=1)
 44     {
 45         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
 46         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
 47         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
 48         for(i=2;i<=n;i++)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
 49     } 
 50     for(i=1,k=0;i<=n;h[rk[i++]]=k)
 51         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];++k);
 52 }
 53 void insert(int nt,int la,int z,int y,int vl)
 54 {
 55     t[nt].sum=t[la].sum+1,t[nt].lc=t[la].lc,t[nt].rc=t[la].rc;
 56     if(z==y)  return;
 57     int mi=z+y>>1;
 58     if(vl<=mi)  t[nt].lc=++cnt,insert(t[nt].lc,t[la].lc,z,mi,vl);
 59     else        t[nt].rc=++cnt,insert(t[nt].rc,t[la].rc,mi+1,y,vl);   
 60 }
 61 bool query(int nt,int la,int z,int y,int le,int ri)
 62 {
 63     if(t[la].sum-t[nt].sum==0)  return 0;
 64     if(z==le&&y==ri)            return 1;
 65     int mi=z+y>>1;
 66     if(ri<=mi)  return query(t[nt].lc,t[la].lc,z,mi,le,ri);
 67     if(le>mi)   return query(t[nt].rc,t[la].rc,mi+1,y,le,ri);
 68     bool p1=query(t[nt].lc,t[la].lc,z,mi,le,mi);
 69     bool p2=query(t[nt].rc,t[la].rc,mi+1,y,mi+1,ri);
 70     return (p1|p2);
 71 }
 72 int main()
 73 {
 74     n=read(),q=read();
 75     scanf("%s",s);
 76     for(int i=1;i<=n;i++)  a[i]=s[i-1]-'a'+1;
 77     suffix();pre();
 78     for(int i=1;i<=n;i++)
 79     {
 80         rt[i]=++cnt;
 81         insert(rt[i],rt[i-1],1,n,rk[i]);
 82     }
 83     for(int i=1;i<=q;i++)
 84     {
 85         a1=read(),a2=read(),a3=read(),a4=read();   
 86         l=0,r=bj(a4-a3+1,a2-a1+1);
 87         while(l<r)
 88         {
 89             mid=(l+r+1)>>1,lm=rm=rk[a3];
 90             for(int j=16;j>=0;j--) 
 91                 if(lm>(1<<j)&&p[lm-(1<<j)+1][j]>=mid)
 92                     lm-=(1<<j);
 93             for(int j=16;j>=0;j--)
 94                 if(rm+(1<<j)<=n&&p[rm+1][j]>=mid)
 95                     rm+=(1<<j);
 96             if(query(rt[a1-1],rt[a2-mid+1],1,n,lm,rm))  l=mid;
 97             else  r=mid-1;
 98         }
 99         printf("%d\n",l);
100     }
101     return 0;
102 }
4556

 

       4.BZOJ2754 喵星球上的点名

        暴力水过去的,并不知道正解应该怎么写。把所有姓名和询问都接成一个串,对每一个询问RMQ求出可行区间,然后就暴力扫可行区间统计答案。实际上复杂度非常没保障。 COGS上数据是学长强化过的,貌似只有cooook过了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 inline int read()
 6 {
 7     int jg=0,jk=getchar()-'0';
 8     while(jk<0||jk>9)    jk=getchar()-'0';
 9     while(jk>=0&&jk<=9)  jg*=10,jg+=jk,jk=getchar()-'0';
10     return jg;
11 }
12 const int sj=300010;
13 const int inf=10001;
14 int h[sj],rk[sj],sa[sj],tx[sj],ty[sj],cnt,q,m,a[sj],n,len,a1,bl[sj],p[sj][20];
15 int st[50010],ln[50010],num[20010],ans,lm,rm;
16 bool r[20010];
17 inline int bj(int x,int y)
18 {
19     return x<y?x:y;
20 }
21 void pre()
22 {
23     for(int i=1;i<=n;i++)  p[i][0]=h[i];
24     for(int j=1;(1<<j)<=n;j++)
25         for(int i=1;i+(1<<j)-1<=n;i++)
26             p[i][j]=bj(p[i][j-1],p[i+(1<<(j-1))][j-1]);
27 }
28 void rsort()
29 {
30     memset(tx,0,sizeof(int)*(m+1));
31     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
32     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
33     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
34 }
35 void suffix()
36 {
37     int i,j,k,l,p;
38     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
39     m=inf,rsort();
40     for(l=1,p=1;p<n;m=p,l<<=1)
41     {
42         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
43         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
44         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
45         for(i=2;i<=n;i++)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
46     }
47     for(i=1,k=0;i<=n;h[rk[i++]]=k)
48         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
49 }
50 int main()
51 {
52     cnt=read(),q=read();
53     for(int i=1;i<=cnt;i++)
54     {
55         len=read();
56         for(int j=1;j<=len;j++)
57             a1=read(),a[++n]=a1,bl[n]=i;
58         a[++n]=inf;
59         len=read();
60         for(int j=1;j<=len;j++)
61             a1=read(),a[++n]=a1,bl[n]=i;
62         a[++n]=inf;
63     }
64     for(int i=1;i<=q;i++)
65     {
66         ln[i]=read(),st[i]=n+1;
67         for(int j=1;j<=ln[i];j++)
68             a1=read(),a[++n]=a1;
69         a[++n]=inf; 
70     }
71     suffix();pre();
72     for(int i=1;i<=q;i++)
73     {
74         lm=rm=rk[st[i]],ans=0;
75         for(int j=19;j>=0;j--)
76             if(lm>(1<<j)&&p[lm-(1<<j)+1][j]>=ln[i])
77                 lm-=(1<<j);
78         for(int j=19;j>=0;j--)
79             if(rm+(1<<j)<=n&&p[rm+1][j]>=ln[i])
80                 rm+=(1<<j);
81         for(int j=lm;j<=rm;j++)
82             if(bl[sa[j]]&&!r[bl[sa[j]]])
83                 r[bl[sa[j]]]=1,num[bl[sa[j]]]++,ans++;
84         for(int j=lm;j<=rm;j++)
85             if(bl[sa[j]])
86                 r[bl[sa[j]]]=0;
87         printf("%d\n",ans);
88     }
89     for(int i=1;i<cnt;i++)
90         printf("%d ",num[i]);
91     printf("%d",num[cnt]);
92     return 0;
93 }
2754

 

       5.BZOJ3230 相似子串

         最重要的还是按照题目里的那个奇怪定义和排序方式求出询问字符串的开始和结尾位置……剩下的倒都是SA+RMQ的套路。开头与结尾不可得兼,取开头而求结尾者也。以下是当初写的注释。

如何根据题意求结尾位置:
题意中越短的子串字典序越靠前,同一个位置开头的子串字典序一定相连
求结尾位置本质上也就是求它在i位置为开头的子串中是第几个
a1-sum[i-1]     是以i开头的子串中排第几
+h[i]                是去掉重复,以i开头的子串原本就没有算这h[i]个
sa[i]                是起始的基准位置
-1                    手调拍出来的QAQ

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #define ll long long
  5 using namespace std;
  6 const int sj=100010;
  7 int tx[sj],ty[sj],sa[sj],rk[sj],a[sj],n,q,p[2][sj][25],rr[sj],m,h[sj];
  8 int l,r,mid,pos1,pos2,pos3,pos4,he[sj],ss[sj];
  9 ll a1,a2,sum[sj],ans,len1,len2;
 10 char s[sj];
 11 void rsort()
 12 {
 13     memset(tx,0,sizeof(int)*(m+1));
 14     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 15     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 16     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 17 }
 18 inline ll read()
 19 {
 20     ll jg=0,jk=getchar()-'0';
 21     while(jk<0||jk>9)    jk=getchar()-'0';
 22     while(jk>=0&&jk<=9)  jg*=10,jg+=jk,jk=getchar()-'0';
 23     return jg;
 24 }
 25 inline int bj(int x,int y)
 26 {
 27     return x<y?x:y;
 28 }
 29 void pre(int op)
 30 {
 31     for(int i=1;i<=n;i++)  p[op][i][0]=h[i];
 32     for(int j=1;(1<<j)<=n;j++)
 33         for(int i=1;i+(1<<j)-1<=n;i++)
 34             p[op][i][j]=bj(p[op][i][j-1],p[op][i+(1<<(j-1))][j-1]);
 35 }
 36 int RMQ(int op,int l,int r)
 37 {
 38     if(l==r) return n;
 39     if(l>r)  swap(l,r);
 40     l++;int k;
 41     for(k=0;(1<<(k+1))<=r-l+1;k++);
 42     return bj(p[op][l][k],p[op][r-(1<<k)+1][k]);
 43 }
 44 void suffix()
 45 {
 46     int i,j,k,l,p;
 47     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
 48     m=26;rsort();
 49     if(n==1)  rk[sa[1]]=1;
 50     for(p=1,l=1;p<n;m=p,l<<=1)
 51     {
 52         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
 53         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
 54         rsort(),swap(ty,rk),rk[sa[1]]=p=1;
 55         for(i=2;i<=n;i++) 
 56             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
 57     }
 58     for(i=1,k=0;i<=n;h[rk[i++]]=k)
 59         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
 60 }
 61 void work()
 62 {
 63     pre(0);
 64     memcpy(rr,rk,sizeof(rk));
 65     memcpy(he,h,sizeof(he));
 66     memcpy(ss,sa,sizeof(ss));
 67     for(int i=1;i<=n;i++)  a[i]=s[n-i]-'a'+1;
 68     for(int i=1;i<=n;i++)  sum[i]=sum[i-1]+n-sa[i]+1-h[i];
 69 }
 70 int getpos(ll x)
 71 {
 72     l=1,r=n;
 73     while(l<r)
 74     {
 75         mid=l+r>>1;
 76         if(sum[mid]<x)  l=mid+1;
 77         else            r=mid;
 78     }
 79     return r;
 80 }
 81 int main()
 82 {
 83     n=read(),q=read();
 84     scanf("%s",s);
 85     for(int i=1;i<=n;i++)  a[i]=s[i-1]-'a'+1;
 86     a[n+1]=-1;
 87     suffix(),work();
 88     suffix(),pre(1);
 89     for(int i=1;i<=(n-1)/2+1;i++)  swap(rk[i],rk[n-i+1]);
 90     for(int i=1;i<=q;i++)
 91     {
 92         a1=read(),a2=read();
 93         if(a1>sum[n]||a2>sum[n])  printf("-1\n");
 94         else
 95         {
 96             pos1=getpos(a1),pos2=getpos(a2);
 97             pos3=ss[pos1]+a1+he[pos1]-sum[pos1-1]-1,pos1=ss[pos1];
 98             pos4=ss[pos2]+a2+he[pos2]-sum[pos2-1]-1,pos2=ss[pos2];
 99             len2=bj(pos3-pos1+1,pos4-pos2+1);
100             len1=bj(RMQ(1,rk[pos3],rk[pos4]),len2);
101             ans=len1*len1;
102             len1=bj(RMQ(0,rr[pos1],rr[pos2]),len2);
103             ans+=len1*len1;
104             printf("%lld\n",ans);
105         }
106     }
107     return 0;
108 }
similar

 

 

       6.BZOJ4199 品酒大会

        充分利用height数组的性质。在n相似的时候没有任何两杯酒满足要求,而0相似的时候任意两杯酒都满足要求;满足要求的总是$rk$在一个区间里的所有酒,两问的答案都可以通过区间计算得到。如果我们从大到小处理,相当于一个区间合并、更新答案的过程。注意有负权存在。刚开始犯蠢写了个主席树,T了之后才发现这完全是并查集就能解决的问题……

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #define ll long long
  6 using namespace std;
  7 const int sj=300010;
  8 int n,m,a[sj],tx[sj],ty[sj],rk[sj],sa[sj],h[sj],a1,a2,fa[sj];
  9 ll vl[sj],ans1[sj],ans2[sj],sum,ans,tp1,tp2,tp,tp3,tp4,qwq,mx[sj],mi[sj],num[sj];
 10 char s[sj];
 11 struct ls
 12 {
 13     int num,val;
 14 }ss[sj];
 15 struct tree
 16 {
 17     int lc,rc,sum;
 18 }t[sj*30];
 19 int comp(const ls&x,const ls&y)
 20 {
 21     return x.val<y.val;
 22 }
 23 void rsort()
 24 {
 25     memset(tx,0,sizeof(int)*(m+1));
 26     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 27     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 28     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 29 }
 30 void suffix()
 31 {
 32     int i,j,k,l,p;
 33     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
 34     m=26;rsort();
 35     for(p=1,l=1;p<n;m=p,l<<=1)
 36     {
 37         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
 38         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
 39         rsort(),swap(ty,rk),rk[sa[1]]=p=1;
 40         for(i=2;i<=n;i++) 
 41             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
 42     }
 43     for(i=1,k=0;i<=n;h[rk[i++]]=k)
 44         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
 45 } 
 46 void init()
 47 {
 48     scanf("%d%s",&n,s);
 49     memset(fa,-1,sizeof(fa));
 50     tp1=-1ll<<62,tp2=-1ll<<62,tp3=1ll<<62,tp4=1ll<<62;
 51     for(int i=1;i<=n;i++)  
 52     {    
 53         scanf("%lld",&vl[i]);
 54         a[i]=s[i-1]-'a'+1;
 55         if(vl[i]>=tp1)  tp2=tp1,tp1=vl[i];  
 56         else if(vl[i]>tp2)  tp2=vl[i];
 57         if(vl[i]<=tp3)  tp4=tp3,tp3=vl[i];
 58         else if(vl[i]<tp4)  tp4=vl[i];  
 59     }
 60 }
 61 int find(int x)
 62 {
 63     if(fa[x]==-1)  return x;
 64     return fa[x]=find(fa[x]);
 65 }
 66 inline ll maxx(ll x,ll y)
 67 {
 68     return x>y?x:y;
 69 }
 70 inline ll minn(ll x,ll y)
 71 {
 72     return x<y?x:y;
 73 }
 74 int main()
 75 {
 76     init();
 77     suffix();
 78     for(int i=1;i<=n;i++)
 79     {
 80         ss[i].num=i,ss[i].val=h[i];
 81         num[i]=1,mx[i]=mi[i]=vl[sa[i]];
 82     }
 83     sort(ss+1,ss+n+1,comp);
 84     ans=-1ll<<62;
 85     for(int i=n;i>=1;i--)
 86     {
 87         if(ss[i].val!=ss[i+1].val)  
 88             ans1[ss[i+1].val]=sum/2,ans2[ss[i+1].val]=ans;
 89         a1=ss[i].num,a2=a1-1;
 90         if(!h[a1])  break;
 91         a1=find(a1),a2=find(a2);
 92         sum-=(ll)num[a1]*(num[a1]-1);
 93         sum-=(ll)num[a2]*(num[a2]-1);
 94         tp=mx[a1]*mx[a2],qwq=mi[a1]*mi[a2];
 95         if(tp>ans)   ans=tp;
 96         if(qwq>ans)  ans=qwq;
 97         if(num[a1]<num[a2])  swap(a1,a2);
 98         mx[a1]=maxx(mx[a1],mx[a2]);
 99         mi[a1]=minn(mi[a1],mi[a2]);
100         num[a1]+=num[a2];
101         fa[a2]=a1;
102         sum+=(ll)num[a1]*(num[a1]-1);
103     }
104     ans1[0]=(ll)n*(n-1)/2,ans2[0]=maxx(tp1*tp2,tp3*tp4);
105     for(int i=0;i<n;i++)
106         printf("%lld %lld\n",ans1[i],ans2[i]);
107     return 0;
108 }
4199

 

posted @ 2018-02-11 21:18  moyiii  阅读(245)  评论(0编辑  收藏  举报