爆零蒟蒻今天终于滚粗了......
也就是说,这是北京集训的最后一篇题解了。之后的比赛,我已经没有访问权限了。
北京集训:20180326

T1:

北京集训:20180326
考虑一个暴力做法:f[i][j]表示字符串区间[i,j]的最大等级。
如果k级字符串[i,j]包含一个k-1级字符串s,且s没有达到[i,j]的首(或者尾)的话,我们去掉[i,j]的首(或者尾),剩下的仍然是一个k级字符串。
所以我们可以暴力dp,f[i][j]=max(f[i+1][j],f[i][j-1],max(f[substr])+1)。
而后面的那个可以用后缀自动机枚举出现两次及以上的子串,总复杂度O(n^3)。
显然这并不是正解(然而这对正解有很大的帮助)(要不然我也不会写他)。
考虑我们这样增量(减量?不!)去掉首尾字符的过程,我们一定能保证一个k级字符串的首尾都是一个k-1级字符串。
尾相同的子串有怎样的性质呢?他会出现在当前串的parent树的祖先上。
于是我们可以用f[i]表示后缀自动机第i个节点及其祖先节点中,最大的等级,g[i]表示取到这个等级,所在的最短的节点。
为什么这样做正确?因为对于结尾更靠后的串,我们不计算他的贡献也没有关系是吧,反正他能转移到的串的子串一定能由一个结尾更靠前的串转移到。
转移显然用最短的节点转移最优。我们暴力找头上的那个串出现的位置,看看是否可行即可。
这样我们get到了n^2暴力......
考虑我们现在复杂度的瓶颈在哪里?维护right集合和查询的过程。于是我们可以反向建立主席树并进行启发式合并,复杂度O(nlogn)。
(其实我感觉这东西时间和空间复杂度都是O(nlog^2n)的,只不过跑不满罢了)
代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #define debug cerr
  7 using namespace std;
  8 const int maxn=4e5+1e2;
  9 
 10 char in[maxn>>1];
 11 int li,ans=1;
 12 
 13 struct PersistentSegmentTree {
 14     static const int maxe = maxn * 25;
 15     int siz[maxe],lson[maxe],rson[maxe],cnt;
 16     
 17     inline void insert(int &pos,int l,int r,const int &tar) {
 18         if( !pos ) pos = ++cnt;
 19         siz[pos] = 1;
 20         if( l == r ) return;
 21         const int mid = ( l + r ) >> 1;
 22         if( tar <= mid ) return insert(lson[pos],l,mid,tar);
 23         else return insert(rson[pos],mid+1,r,tar);
 24     }
 25     inline int merge(int p1,int p2,int l,int r) {
 26         if( ! ( siz[p1] && siz[p2] ) ) return siz[p1] ? p1 : p2;
 27         int ret = ++cnt; siz[ret] = siz[p1] + siz[p2];
 28         if( l == r ) return ret;
 29         const int mid = ( l + r ) >> 1;
 30         lson[ret] = merge(lson[p1],lson[p2],l,mid);
 31         rson[ret] = merge(rson[p1],rson[p2],mid+1,r);
 32         return ret;
 33     }
 34     inline int query(int pos,int l,int r,const int &ll,const int &rr) {
 35         if( !pos ) return 0;
 36         if( ll <= l && r <= rr ) return siz[pos];
 37         const int mid = ( l + r ) >> 1;
 38         if( rr <= mid ) return query(lson[pos],l,mid,ll,rr);
 39         if( ll > mid ) return query(rson[pos],mid+1,r,ll,rr);
 40         return query(lson[pos],l,mid,ll,rr) + query(rson[pos],mid+1,r,ll,rr);
 41     }
 42 }tree;
 43 
 44 namespace SAM {
 45     int ch[maxn][26],fa[maxn],len[maxn],deg[maxn],last,root,cnt;
 46     int rit[maxn],pos[maxn],roots[maxn],bst[maxn],f[maxn];
 47     int seq[maxn],qlen;
 48     
 49     inline int NewNode(int li) {
 50         len[++cnt] = li;
 51         return cnt;
 52     }
 53     inline void extend(int x,int at) {
 54         int p = last;
 55         int np = NewNode(len[p]+1); rit[np] = pos[np] = at;
 56         while( p && !ch[p][x] ) ch[p][x] = np , p = fa[p];
 57         if( !p ) fa[np] = root;
 58         else {
 59             int q = ch[p][x];
 60             if( len[q] == len[p] + 1 ) fa[np] = q;
 61             else {
 62                 int nq = NewNode(len[p]+1);
 63                 memcpy(ch[nq],ch[q],sizeof(ch[q])) , fa[nq] = fa[q] , pos[nq] = pos[q];
 64                 fa[np] = fa[q] = nq;
 65                 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p];
 66             }
 67         }
 68         last = np;
 69     }
 70     inline void build() {
 71         last = root = NewNode(0);
 72         for(int i=1;i<=li;i++) extend(in[i]-'a',i);
 73     }
 74     inline void topo() {
 75         for(int i=1;i<=cnt;i++) if( fa[i] ) ++deg[fa[i]];
 76         queue<int> q;
 77         for(int i=1;i<=cnt;i++) if( !deg[i] ) q.push(i);
 78         while( q.size() ) {
 79             const int pos = q.front(); q.pop() , seq[++qlen] = pos;
 80             if( pos == root ) continue;
 81             if( rit[pos] ) {
 82                 int t = 0;
 83                 tree.insert(t,1,li,rit[pos]);
 84                 roots[pos] = tree.merge(roots[pos],t,1,li);
 85             }
 86             roots[fa[pos]] = tree.merge(roots[fa[pos]],roots[pos],1,li);
 87             if( !--deg[fa[pos]] ) q.push(fa[pos]);
 88         }
 89         reverse(seq+1,seq+1+qlen);
 90     }
 91     inline void getans() {
 92         f[root] = 1 , bst[root] = root;
 93         for(int i=2;i<=qlen;i++) {
 94             const int now = seq[i] , milen = len[fa[now]] + 1;
 95             if( fa[now] == root ) {
 96                 f[now] = 1 , bst[now] = now;
 97             } else {
 98                 bst[now] = bst[fa[now]] , f[now] = f[bst[now]];
 99                 const int ql = pos[now] - len[now] + len[bst[now]];
100                 const int qr = pos[now] - milen + len[bst[now]];
101                 if( tree.query(roots[bst[now]],1,li,ql,qr) ) f[now]++ , bst[now] = now;
102             }
103             ans = max( ans , f[now] );
104         }
105     }
106 }
107 
108 int main() {
109     scanf("%s",in+1) , li = strlen(in+1);
110     SAM::build() , SAM::topo();
111     SAM::getans();
112     printf("%d\n",ans);
113     return 0;
114 }
View Code

相关文章:

  • 2021-10-09
  • 2022-01-17
  • 2021-06-13
  • 2022-01-27
  • 2022-01-14
  • 2021-09-13
  • 2021-09-18
  • 2021-08-11
猜你喜欢
  • 2021-04-20
  • 2021-08-14
  • 2022-01-20
  • 2021-09-16
  • 2021-11-27
  • 2021-05-02
  • 2021-09-29
相关资源
相似解决方案