zoukankan      html  css  js  c++  java
  • Bzoj4480: [Jsoi2013]快乐的jyy 广义后缀自动机 倍增 哈希 manacher

    国际惯例的题面:

    有人说这是回文自动机的板子题,然而我是不会这种东西的。
    于是,我选择用更一般性的方法去解决这个题,就是那一堆东西了。
    首先,我们把两个串同时插入一个广义SAM里,拓扑排序维护每个节点的parent树的子树中来自两个串的right集合的大小sizA和sizB。
    同时倍增求出parent树上每个节点向上2^k层的父亲是哪个节点。
    显然一个串本质不同的回文串数量是O(n)的(什么你不知道?manacher的复杂度怎么证的?),我们对A串做manacher,在暴力拓展的时候,去后缀自动机上倍增查询这个包含这个串的最浅的节点(显然这个节点的right集合最大),这个串对答案的贡献就是这个节点的sizA*sizB了。
    为了防止同样的串被统计多次,我们需要哈希和unordered_set去重。
    这个题的广义SAM在建立的时候,无论如何要新建节点,不能走已有的节点,否则会导致一些节点的len变小,出现一些不合法的情况。
    总体时间复杂度O(nlogn),由于这种做法常数较大,所以BZOJ光荣垫底(然而AC这题还是绰绰有余的)。

    代码:

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 #include<queue>
      5 #include<tr1/unordered_set>
      6 #define debug cout
      7 typedef long long int lli;
      8 typedef unsigned long long int ulli;
      9 using namespace std;
     10 using namespace tr1;
     11 const int maxn=5e4+1e2,maxl=20;
     12 const ulli base = 29;
     13 
     14 char a[maxn],b[maxn];
     15 int la,lb;
     16 int rec[maxn];
     17 lli ans;
     18 
     19 struct ExtendedSuffixAutomatic {
     20     int ch[maxn<<2][26],len[maxn<<2],fa[maxn<<2],anc[maxn<<2][maxl];
     21     int siz[maxn<<2][2],deg[maxn<<2],root,cnt;
     22     inline int NewNode(int ll) {
     23         return len[++cnt] = ll , cnt;
     24     }
     25     ExtendedSuffixAutomatic() { root = NewNode(0); }
     26     inline void extend(int x,int p) {
     27         int np = NewNode(len[p]+1);
     28         while( p && !ch[p][x] ) ch[p][x] = np , p = fa[p];
     29         if( !p ) fa[np] = root;
     30         else {
     31             int q = ch[p][x];
     32             if( len[q] == len[p] + 1 ) fa[np] = q;
     33             else {
     34                 int nq = NewNode(len[p]+1);
     35                 memcpy(ch[nq],ch[q],sizeof(ch[q])) , fa[nq] = fa[q];
     36                 fa[np] = fa[q] = nq;
     37                 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p];
     38             }
     39         }
     40     }
     41     inline void Ex_extend(char* s,int li,int bel) {
     42         int cur = root;
     43         for(int i=1;i<=li;i++) {
     44             extend(s[i]-'A',cur) , cur = ch[cur][(int)s[i]-'A']; // a's A is different with b's A .
     45             ++siz[cur][bel];
     46             if( !bel ) rec[i] = cur;
     47         }
     48     }
     49     inline void topo() {
     50         for(int i=1;i<=cnt;i++) if( fa[i] ) ++deg[fa[i]];
     51         queue<int> q;
     52         for(int i=1;i<=cnt;i++) if( !deg[i] ) q.push(i);
     53         while( q.size() ) {
     54             const int pos = q.front(); q.pop();
     55             if( pos == root ) break;
     56             anc[pos][0] = fa[pos];
     57             for(int i=0;i<2;i++) siz[fa[pos]][i] += siz[pos][i];
     58             if( !--deg[fa[pos]] ) q.push(fa[pos]);
     59         }
     60         for(int j=1;j<20;j++) for(int i=1;i<=cnt;i++) anc[i][j] = anc[anc[i][j-1]][j-1];
     61     }
     62     inline lli query(int pos,int lim) {
     63         for(int j=19;~j;j--) if( len[anc[pos][j]] >= lim ) pos = anc[pos][j];
     64         return (lli) siz[pos][0] * siz[pos][1];
     65     }
     66 }esam;
     67 
     68 struct Hash {
     69     ulli pows[maxn],h[maxn];
     70     inline void build(char* s,int li) {
     71         *pows = 1;
     72         for(int i=1;i<=li;i++) pows[i] = pows[i-1] * base , h[i] = h[i-1] * base + s[i] - 'A' + 1;
     73     }
     74     inline ulli query(int l,int r) {
     75         return h[r] - h[l-1] * pows[r-l+1];
     76     }
     77 }hsh;
     78 
     79 unordered_set<ulli> vis;
     80 
     81 inline void calc(int al,int ar) {
     82     ulli h = hsh.query(al,ar);
     83     if( vis.find(h) != vis.end() ) return;
     84     vis.insert(h) , ans += esam.query(rec[ar],ar-al+1);
     85 }
     86 
     87 inline void manacher(char* s,int li) {
     88     static char in[maxn<<1];
     89     static int f[maxn<<1],app[maxn<<1],len,pos,mxr;
     90     #define getpos_l(i) (app[i]|app[i+1])
     91     #define getpos_r(i) (app[i]|app[i-1])
     92     *in = '$';
     93     for(int i=1;i<=li;i++) in[++len] = s[i] , app[len] = i , in[++len] = '#';
     94     for(int i=1;i<=len;i++) {
     95         if( i < mxr ) f[i] = min( f[pos*2-i] , mxr - i );
     96         else f[i] = 1;
     97         if( i & 1 ) calc(getpos_l(i-f[i]+1),getpos_r(i+f[i]-1));
     98         while( in[i-f[i]] == in[i+f[i]] ) {
     99             ++f[i];
    100             calc(getpos_l(i-f[i]+1),getpos_r(i+f[i]-1));
    101         }
    102         if( i + f[i] > mxr ) mxr = i + f[i] , pos = i;
    103     }
    104     #undef getpos_l
    105     #undef getpos_r
    106 }
    107 
    108 int main() {
    109     scanf("%s%s",a+1,b+1) , la = strlen(a+1) , lb = strlen(b+1);
    110     esam.Ex_extend(a,la,0) , esam.Ex_extend(b,lb,1) , esam.topo() , hsh.build(a,la);
    111     manacher(a,la) , printf("%lld
    ",ans);
    112     return 0;
    113 }
    View Code



    この校舎がつくる影
    教学楼所组成的影子
    待ち合わせした音楽室
    在音乐教室中等候
    屋上から見えた 流れてくひこうき雲
    从屋顶上看到的划过天际的飞行云
    まだ残ってる落書き
    还残留着的涂鸦
    この瞳に映るすべて
    映入眼帘的一切
    伝えたい ひとつひとつに
    想要传达 一个一个
    想い出溢れること
    满溢的思念
    部室の窓から探してた
    从活动室窗户寻找
    遠くても君なら すぐにみつけられる
    即使多么遥远你也立刻找出

  • 相关阅读:
    centos7 双网口绑定
    centos docker 修改默认存储路径
    Django 操作已经存在的数据库
    package ‘RPMM’ is not available (for R version 3.6.0)
    关于tornado的raise gen.Retuen()
    tornodo学习之路
    关于微信小程序登录机制
    关于微信小程序更新机制
    过渡结束事件
    移动端动效
  • 原文地址:https://www.cnblogs.com/Cmd2001/p/9201331.html
Copyright © 2011-2022 走看看