• Bzoj2534:后缀自动机 主席树启发式合并


    国际惯例的题面:

    考虑我们求解出字符串uvu第一个u的右端点为i,第二个u的右端点为j,我们需要满足什么性质?
    显然j>i+L,因为我们选择的串不能是空串。
    另外考虑i和j的最长公共前缀(也就是说其parent树上lca的len),为了保证他们相同,我们需要:
    j-len>=i-L。
    整理一下,如果我们已知i,j需要在区间[i+L+1,i+L+len]中。
    如果我们已知j,i需要在区间[j-L-len,j-L-1]中。
    于是我们可以写n^2暴力了:暴力维护parent上每个节点的right集合,对于每个i,暴力向上跳,暴力找可行的j。
    然后我们发现,维护right集合可以用主席树启发式合并做,对于计算贡献,我们可以先枚举lca,然后计算有多少组可行的i,j。
    在第二个计算的时候,我们显然是会在两颗主席树进行合并的时候进行计算,于是我们可以把较小的那颗拍扁,在另外一棵里暴力查询每一个值的贡献。
    这样总复杂度O(nlog^2n),轻松AC。
    注意主席树启发式合并的时间和空间复杂度都是O(nlog^n)的,因为考虑每层摊还下来只会被新建logn次(我已经把长度和节点个数乘起来了)。
    另外这题字符集大小为全体可见字符,所以需要用map存后缀自动机。
    (不是很明白为什么网上那么多题解都是后缀数组的,明明后缀自动机这么好写(不会后缀数组的就不要说话了.jpg))

    代码:

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<map>
      4 #include<queue>
      5 using namespace std;
      6 const int maxn=1e5+1e2,maxl=18;
      7  
      8 char in[maxn>>1];
      9 int li,lim;
     10 int seq[maxn>>1],seqlen;
     11 long long ans;
     12  
     13 struct PersistentSegmentTree {
     14     int lson[maxn*maxl<<1],rson[maxn*maxl<<1],siz[maxn*maxl<<1],cnt;
     15     inline void insert(int &pos,int l,int r,int tar) {
     16         if( !pos ) pos = ++cnt; siz[pos] = 1;
     17         if( l == r ) return;
     18         const int mid = ( l + r ) >> 1;
     19         if( tar <= mid ) insert(lson[pos],l,mid,tar);
     20         else insert(rson[pos],mid+1,r,tar);
     21     }
     22     inline int merge(int p1,int p2,int l,int r) {
     23         if( ! ( siz[p1] && siz[p2] ) ) return siz[p1] ? p1 : p2;
     24         int ret = ++cnt; siz[ret] = siz[p1] + siz[p2];
     25         if( l == r ) return ret;
     26         const int mid = ( l + r ) >> 1;
     27         lson[ret] = merge(lson[p1],lson[p2],l,mid) ,
     28         rson[ret] = merge(rson[p1],rson[p2],mid+1,r) ;
     29         return ret;
     30     }
     31     inline int query(int pos,int l,int r,const int &ll,const int &rr) {
     32         if( !pos ) return 0;
     33         if( ll <= l && r <= rr ) return siz[pos];
     34         const int mid = ( l + r ) >> 1;
     35         if( rr <= mid ) return query(lson[pos],l,mid,ll,rr);
     36         else if( ll > mid ) return query(rson[pos],mid+1,r,ll,rr);
     37         return query(lson[pos],l,mid,ll,rr) + query(rson[pos],mid+1,r,ll,rr);
     38     }
     39     inline void dfs(int pos,int l,int r) {
     40         if( !pos ) return;
     41         if( l == r ) return void(seq[++seqlen]=l);
     42         const int mid = ( l + r ) >> 1;
     43         dfs(lson[pos],l,mid) , dfs(rson[pos],mid+1,r);
     44     }
     45     inline int getsiz(int pos) {
     46         return siz[pos];
     47     }
     48 }segt;
     49  
     50 namespace SAM {
     51     int fa[maxn],len[maxn],deg[maxn],last,root,cnt;
     52     int rit[maxn],roots[maxn];
     53     map<int,int> ch[maxn];
     54     inline int NewNode(int ll) {
     55         len[++cnt] = ll;
     56         return cnt;
     57     }
     58     inline int extend(int x,int rr) {
     59         int p = last;
     60         int np = NewNode(len[p]+1); rit[np] = rr;
     61         while( ch[p].find(x) == ch[p].end() ) ch[p][x] = np , p = fa[p];
     62         if( !p ) fa[np] = root;
     63         else {
     64             int q = ch[p][x];
     65             if( len[q] == len[p] + 1 ) fa[np] = q;
     66             else {
     67                 int nq = NewNode(len[p]+1);
     68                 ch[nq] = ch[q] , fa[nq] = fa[q];
     69                 fa[np] = fa[q] = nq;
     70                 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p];
     71             }
     72         }
     73         return last = np;
     74     }
     75     inline int query(int root,int i,int samelen) {
     76         if( samelen < 1 ) return 0;
     77         int ret = segt.query(root,1,li,i+lim+1,i+lim+samelen);
     78         if( i - lim - 1 > 0 ) ret += segt.query(root,1,li,i-lim-samelen,i-lim-1);
     79         return ret;
     80     }
     81     inline void topo() {
     82         for(int i=1;i<=cnt;i++) if( fa[i] ) ++deg[fa[i]];
     83         queue<int> q;
     84         for(int i=1;i<=cnt;i++) if( !deg[i] ) q.push(i);
     85         while( q.size() ) {
     86             const int pos = q.front(); q.pop();
     87             if( pos == root ) continue;
     88             if( rit[pos] ) {
     89                 ans += query(roots[pos],rit[pos],len[pos]);
     90                 int t = 0; segt.insert(t,1,li,rit[pos]);
     91                 roots[pos] = segt.merge(roots[pos],t,1,li);
     92             }
     93             if( segt.getsiz(roots[fa[pos]]) < segt.getsiz(roots[pos]) ) // We won't use roots[pos] again .
     94                 swap(roots[fa[pos]],roots[pos]);
     95             seqlen = 0 , segt.dfs(roots[pos],1,li);
     96             for(int i=1;i<=seqlen;i++) ans += query(roots[fa[pos]],seq[i],len[fa[pos]]);
     97             roots[fa[pos]] = segt.merge(roots[fa[pos]],roots[pos],1,li);
     98             if( !--deg[fa[pos]] ) q.push(fa[pos]);
     99         }
    100     }
    101 }
    102  
    103 int main() {
    104     scanf("%d%s",&lim,in+1) , li = strlen(in+1);
    105     SAM::last = SAM::root = SAM::NewNode(0);
    106     for(int i=1;i<=li;i++) SAM::extend(in[i],i);
    107     SAM::topo();
    108     printf("%lld
    ",ans);
    109     return 0;
    110 }
    View Code
  • 相关阅读:
    php 之fsockopen(转)
    【javascript基础】之BigPipe学习研究【转】
    【javascript基础】之浏览器的时钟精度【转】
    【javascript基础】IE6IE9不支持table.innerHTML的解决方法分享【转】
    github报错 please open the options menu from the dashboard and update your name and email
    泡泡堂如何申请小鸡号
    【javascript基础】各浏览器Iframe对contentWindow、contentDocument、document及frames属性测试 【转】
    【javascript基础】之浅析XSS(Cross Site Script)漏洞原理【转】
    【html】(X)HTML语义与元素名全称(部分)【转】
    【javascript基础】渐进式jpeg(progressive jpeg)图片及其相关【转】
  • 原文地址:https://www.cnblogs.com/Cmd2001/p/8697903.html
Copyright © 2020-2023  润新知