2020寒假专题训练:字符串

DTOJ #4734:Incomparable Pairs

【题解】

考虑容斥,计算子串 $a$ 为子串 $b$ 的子串数量.

对 $s$ 建 SAM,对于每一个一个本质不同的子串,计算它包含的本质不同的子串个数之和就是答案.

一个子串可能出现多次,任意选取一次就可以计算答案. 而对于自动机上的一个节点,它产生的贡献为 $s_{\min,r},s_{\min+1,r},\dots,s_{\max,r}$ 中的本质不同的子串.

设 $p_i$ 表示从 $i$ 开始,到现在 $r$ 的本质不同子串的个数(位置不同的相同子串只记录最后一次),可以对每一个本质不同的字串,在它最后一次出现的左端点 $+1$.

每插入一个字符,就算一次新增的自动机节点的贡献,当 $r$ 变大的时候,在 parent 树上从新增节点到根的最后出现位置都会更改。相当于把 parent 树上这一条链染色. 用 LCT 维护.

发现我们要求的是后缀和后的区间和,可以在线段树上区间加等差数列.

发现 SAM 上节点可能分裂,这里查询答案的时候用原来的大小,查询这个节点原来的 $[R-\max+1,R-\min+1]$. 累加就是答案.

效率 $O(n \log^2 n)$.

【代码】

  1 #include<bits/stdc++.h>
  2 const int maxn=200000+10;
  3 int n,pos[maxn],root,last,size,nxt[maxn][27],min[maxn],max[maxn],fa[maxn],ch[maxn][2],val[maxn];
  4 char s[maxn];
  5 struct tree { long long val,tag1,tag2; } t[maxn<<2];
  6 #define ls (k<<1)
  7 #define rs (k<<1|1)
  8 #define mid ((l+r)>>1)
  9 inline void modify1 ( int k,int len,long long w ) { t[k].val+=w*len;t[k].tag1+=w; }
 10 inline void modify2 ( int k,int len,long long l,long long w ) { t[k].val+=l*len+1LL*len*(len+1)/2*w;t[k].tag1+=l;t[k].tag2+=w; }
 11 inline void pushdown ( int k,int len )
 12 {
 13     if ( t[k].tag1 ) modify1(ls,(len+1)>>1,t[k].tag1),modify1(rs,len>>1,t[k].tag1),t[k].tag1=0;
 14     if ( t[k].tag2 ) modify2(ls,(len+1)>>1,t[k].tag2*(len>>1),t[k].tag2),modify2(rs,len>>1,0,t[k].tag2),t[k].tag2=0;
 15 }
 16 inline void update1 ( int k,int l,int r,int ql,int qr,long long w )
 17 {
 18     if ( ql<=l and r<=qr ) { modify1(k,r-l+1,w);return; }
 19     pushdown(k,r-l+1);
 20     if ( ql<=mid ) update1(ls,l,mid,ql,qr,w);
 21     if ( qr>mid ) update1(rs,mid+1,r,ql,qr,w);
 22     t[k].val=t[ls].val+t[rs].val;
 23 }
 24 inline void update2 ( int k,int l,int r,int ql,int qr,long long w )
 25 {
 26     if ( ql<=l and r<=qr ) { modify2(k,r-l+1,(qr-r)*w,w);return; }
 27     pushdown(k,r-l+1);
 28     if ( ql<=mid ) update2(ls,l,mid,ql,qr,w);
 29     if ( qr>mid ) update2(rs,mid+1,r,ql,qr,w);
 30     t[k].val=t[ls].val+t[rs].val;
 31 }
 32 inline void change ( int l,int r,long long v )
 33 {
 34     if ( l>1 ) update1(1,1,n,1,l-1,(r-l+1)*v);
 35     if ( l>0 and r>0 and l<=r ) update2(1,1,n,l,r,v);
 36 }
 37 inline long long query ( int k,int l,int r,int ql,int qr )
 38 {
 39     if ( ql<=l and r<=qr ) return t[k].val;
 40     pushdown(k,r-l+1);long long res=0;
 41     if ( ql<=mid ) res+=query(ls,l,mid,ql,qr);
 42     if ( qr>mid ) res+=query(rs,mid+1,r,ql,qr);
 43     return res;
 44 }
 45 #undef ls
 46 #undef rs
 47 #undef mid
 48 #define ls(x) ch[x][0]
 49 #define rs(x) ch[x][1]
 50 inline void pushdown ( int x ) { val[ls(x)]=val[rs(x)]=val[x]; }
 51 inline int get ( int x,int k=1 ) { return ch[fa[x]][k]==x; }
 52 inline bool nroot ( int x ) { return get(x,0) || get(x,1); }
 53 inline void rotate ( int x )
 54 {
 55     int y=fa[x],z=fa[y],k=get(x);
 56     if ( nroot(y) ) ch[z][get(y)]=x;
 57     fa[ch[y][k]=ch[x][!k]]=y,ch[x][!k]=y;
 58     fa[y]=x,fa[x]=z;
 59 }
 60 int st[maxn],tp;
 61 inline void splay ( int x )
 62 {
 63     st[tp=1]=x;
 64     for ( int y=x;nroot(y);st[++tp]=y=fa[y] ) ;
 65     for ( ;tp;tp-- ) pushdown(st[tp]);
 66     for ( ;nroot(x);rotate(x) ) if ( nroot(fa[x]) ) (get(x)^get(fa[x])) ? rotate(x) : rotate(fa[x]);
 67 }
 68 inline void access ( int x,int v )
 69 {
 70     for ( int y=0;x; ) splay(x),rs(x)=y,change(val[x]-max[x]+1,val[x]-max[fa[x]],-1),y=x,x=fa[x];
 71     splay(1);val[1]=v;change(1,v,1);
 72 }
 73 unsigned long long num,ans;
 74 inline int extend ( int c )
 75 {
 76     int np=++size,p=last;max[np]=max[p]+1;last=np;
 77     while ( p and !nxt[p][c] ) nxt[p][c]=np,p=fa[p];
 78     if ( !p ) fa[np]=root;
 79     else
 80     {
 81         int q=nxt[p][c];
 82         if ( max[q]==max[p]+1 ) fa[np]=q;
 83         else
 84         {
 85             int nq=++size;max[nq]=max[p]+1;fa[nq]=fa[q];
 86             fa[q]=fa[np]=nq;memcpy(nxt[nq],nxt[q],sizeof(nxt[nq]));
 87             while ( p and nxt[p][c]==q ) nxt[p][c]=nq,p=fa[p];
 88         }
 89     }
 90     num+=max[np]-max[fa[np]];min[np]=max[fa[np]]+1;
 91     return np;
 92 }
 93 signed main()
 94 {
 95     scanf(" %s",s+1);n=strlen(s+1);root=last=++size;
 96     for ( int i=1;i<=n;i++ ) pos[i]=extend(s[i]^96);
 97     for ( int i=1;i<=n;i++ ) access(pos[i],i),ans+=query(1,1,n,i-max[pos[i]]+1,i-min[pos[i]]+1);
 98     if ( num&1 ) num=(num+1)/2*num;
 99     else num=num/2*(num+1);
100     return !printf("%llu\n",num-ans);
101 }
DTOJ4734

DTOJ #4730:匹配

【题解】

显然在 AC 自动机上用矩阵快速幂转移 dp. 记 $t=\sum{|t_i|}$,$\Sigma$ 为字符集,时间复杂度为 $O(n\log n+nt|\Sigma|+nt^3\log n+Qt^2\log n)$.

考虑优化倍增预处理部分:跳 lca 时,设当前在 $x$,先跳 $2^{\operatorname{lowbit}(\operatorname{dep}_x)}$ 至 $y$,再跳 $2^{\operatorname{lowbit}(\operatorname{dep}_y)}\cdots$,这样只用处理 $i\in[0,\operatorname{lowbit}(\operatorname{dep}_x)]$ 的 $2^i$ 矩阵.

但这样还是容易被卡(如构造很多点深度都是 $1024$ 的树),所以可以考虑在根节点上加若干点来避免这种问题. 从最低点开始考虑,如果深度为偶数的点多于奇数,可以在根上加一个点,从而保证深度为奇数的点不少于深度为偶数的,同理去考虑其它二进制位. 可知 $O(t^3)$ 次乘法的次数是 $\frac{n}{2}\times 1+\frac{n}{4}\times 2+\cdots<2n=O(n)$. 所以总时间复杂度降到了 $O(n\log n + nt|\Sigma|+nt^3+Qt^2\log n)$.

【代码】

  1 #include<bits/stdc++.h>
  2 const int mod=998244353;
  3 int size,ch[50][30],fail[50],Log[5000],Low[5000],cnt[5000];
  4 int anc[5000][20],n,m,Q,dep[5000],h[5000],e_cnt,root=1,k,add;
  5 bool flag[50];
  6 struct edge { int v,nxt,w; } e[10000];
  7 struct matrix
  8 {
  9     int a[50][50];
 10     inline int * operator [] ( const int x ) { return a[x]; }
 11     matrix(){memset(a,0,sizeof(a));}
 12     inline friend matrix operator * ( matrix A,matrix B )
 13     {
 14         matrix C;
 15         for ( int i=0;i<=size;i++ ) for ( int k=0;k<=size;k++ ) for ( int j=0;j<=size;j++ ) C[i][j]=(C[i][j]+1LL*A[i][k]*B[k][j])%mod;
 16         return C;
 17     }
 18 };
 19 std::vector<matrix> U[3000],D[3000];
 20 inline matrix make_matrix ( int trans )
 21 {
 22     std::vector<int> tr;matrix T;
 23     for ( int i=1;i<=26;i++ ) if ( trans&(1<<i) ) tr.push_back(i);
 24     for ( int i=0;i<=size;i++ ) for ( int j:tr ) T[i][ch[i][j]]++;
 25     return T;
 26 }
 27 inline void dfs ( int u,int fr,int trans )
 28 {
 29     dep[u]=dep[anc[u][0]=fr]+1;
 30     U[u].push_back(make_matrix(trans));
 31     D[u].push_back(make_matrix(trans));
 32     for ( int i=1;anc[u][i-1];i++ ) anc[u][i]=anc[anc[u][i-1]][i-1];
 33     for ( int i=1;i<=Low[dep[u]];i++ )
 34         U[u].push_back(U[u][i-1]*U[anc[u][i-1]][i-1]),
 35         D[u].push_back(D[anc[u][i-1]][i-1]*D[u][i-1]);
 36     for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) dfs(e[i].v,u,e[i].w);
 37 }
 38 inline int lca ( int u,int v )
 39 {
 40     if ( dep[u]<dep[v] ) std::swap(u,v);
 41     for ( int i=Log[dep[u]];~i;i-- ) if ( dep[anc[u][i]]>=dep[v] ) u=anc[u][i];
 42     if ( u==v ) return u;
 43     for ( int i=Log[dep[u]];~i;i-- ) if ( anc[u][i]!=anc[v][i] ) u=anc[u][i],v=anc[v][i];
 44     return anc[u][0];
 45 }
 46 inline void addedge ( int u,int v,int w )
 47 {
 48     e[++e_cnt].nxt=h[u];e[h[u]=e_cnt].v=v;e[e_cnt].w=w;
 49     e[++e_cnt].nxt=h[v];e[h[v]=e_cnt].v=u;e[e_cnt].w=w;
 50 }
 51 signed main()
 52 {
 53     scanf("%d%d%d",&n,&m,&Q);
 54     for ( int i=2;i<=n;i++ )
 55     {
 56         int u,v,val=0;char s[30]={'\0'};
 57         scanf("%d%d %s",&u,&v,s+1);
 58         for ( int i=1;s[i];i++ ) val|=1<<(s[i]^96);
 59         addedge(u,v,val);
 60     }
 61     std::queue<int> q;q.push(1);
 62     while ( !q.empty() )
 63     {
 64         int u=q.front();q.pop();
 65         for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=anc[u][0] ) dep[e[i].v]=dep[anc[e[i].v][0]=u]+1,q.push(e[i].v);
 66     }
 67     for ( int i=1;i<=n;i++ ) cnt[dep[i]]++,k=std::max(k,dep[i]);
 68     for ( int S=0;(1<<S)<=k and S<=9;S++ )
 69     {
 70         int tot[2]={0},U=(1<<S)-1;
 71         for ( int i=0;i<=k;i++ ) if ( (i&U)==add ) tot[((i+add)>>S)&1]+=cnt[i];
 72         if ( tot[0]>tot[1] ) add+=1<<S;
 73     }
 74     if ( add )
 75     {
 76         root=n+add;
 77         for ( int i=add;i>1;i-- ) addedge(n+i,n+i-1,0);
 78         addedge(n+1,1,0);
 79     }
 80     n+=add;
 81     for ( int i=2;i<=n;i++ ) Log[i]=Log[i>>1]+1,Low[i]=Log[i&(-i)];
 82     for ( int i=1;i<=m;i++ )
 83     {
 84         char s[50]={'\0'};scanf(" %s",s+1);
 85         int u=0;
 86         for ( int i=1;s[i];i++ )
 87         {
 88             if ( !ch[u][s[i]^96] ) ch[u][s[i]^96]=++size;
 89             u=ch[u][s[i]^96];
 90         }
 91         flag[u]=true;
 92     }
 93     for ( int i=1;i<=26;i++ ) if ( ch[0][i] ) q.push(ch[0][i]);
 94     while ( !q.empty() )
 95     {
 96         int u=q.front();q.pop();
 97         for ( int i=1;i<=26;i++ )
 98             if ( ch[u][i] ) fail[ch[u][i]]=ch[fail[u]][i],flag[ch[u][i]]|=flag[fail[ch[u][i]]],q.push(ch[u][i]);
 99             else ch[u][i]=ch[fail[u]][i];
100     }
101     for ( int i=0;i<=size;i++ ) if ( flag[i] ) for ( int j=1;j<=26;j++ ) ch[i][j]=i;
102     dep[0]=-1;dfs(root,0,0);
103     while ( Q-- )
104     {
105         int u,v,ans=0,f[50]={0},g[50]={0};scanf("%d%d",&u,&v);int l=lca(u,v);
106         std::vector<matrix> tru,trv;
107         for ( int x=u,k;x!=l;x=anc[x][k] ) k=std::min(Low[dep[x]],Log[dep[x]-dep[l]-1]),tru.push_back(U[x][k]);
108         for ( int x=v,k;x!=l;x=anc[x][k] ) k=std::min(Low[dep[x]],Log[dep[x]-dep[l]-1]),trv.push_back(D[x][k]);
109         std::reverse(trv.begin(),trv.end());f[0]=1;
110         for ( auto A:tru )
111         {
112             for ( int i=0;i<=size;i++ ) g[i]=0;
113             for ( int k=0;k<=size;k++ ) for ( int j=0;j<=size;j++ ) g[j]=(g[j]+1LL*f[k]*A[k][j])%mod;
114             for ( int i=0;i<=size;i++ ) f[i]=g[i];
115         }
116         for ( auto A:trv )
117         {
118             for ( int i=0;i<=size;i++ ) g[i]=0;
119             for ( int k=0;k<=size;k++ ) for ( int j=0;j<=size;j++ ) g[j]=(g[j]+1LL*f[k]*A[k][j])%mod;
120             for ( int i=0;i<=size;i++ ) f[i]=g[i];
121         }
122         for ( int i=0;i<=size;i++ ) if ( flag[i] ) ans=(ans+f[i])%mod;
123         printf("%d\n",ans);
124     }
125     return 0;
126 }
DTOJ4730

DTOJ #4723:子串 / DTOJ #4670:浑水摸鱼

【题解】

考虑 hash 判断. 设第 $i$ 位的字母的后一个位置为 $nxt_i$,权值为 $nxt_i-i$,即可 hash.

用可持久化线段树维护 hash 然后直接将所有后缀排序即可. 则答案为 $\frac{n(n+1}{2}-\sum\limits_{i=1}^{n-1} lcp(s_i,s_{i+1})$.

效率 $O(5 n \log^3 n)$. 如果常数非常优秀能过(注意不同二分方式造成的巨大常数差). 卡卡常就过了.

考虑优化,用可持久化块状数组维护即可. 效率 $O(5 n \log n \sqrt n)$.

【代码】

 1 #include<bits/stdc++.h>
 2 inline int read ( void )
 3 {
 4     int x=0;char ch;
 5     while ( !isdigit(ch=getchar()) ) ;
 6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
 7     return x;
 8 }
 9 #define maxn 50010
10 #define Hash 1000000007
11 #define HASH unsigned long long
12 struct tree { int ls,rs;HASH val; } t[maxn*30];
13 int n,a[maxn],root[maxn],p[maxn],nxt[maxn],tot;
14 HASH Pow[maxn];std::set<int> s[maxn];
15 inline void modify ( int &k,int fr,int l,int r,int p,HASH w )
16 {
17     t[k=++tot]=t[fr];t[k].val+=w; 
18     if ( l==r ) return;int mid=(l+r)>>1;
19     if ( p<=mid ) modify(t[k].ls,t[fr].ls,l,mid,p,w);
20     else modify(t[k].rs,t[fr].rs,mid+1,r,p,w);
21 }
22 inline HASH query ( int k,int l,int r,int ql,int qr )
23 {
24     if ( !k or ( ql<=l and r<=qr ) ) return t[k].val;
25     HASH res=0;int mid=(l+r)>>1;
26     if ( ql<=mid and t[k].ls ) res+=query(t[k].ls,l,mid,ql,qr);
27     if ( qr>mid and t[k].rs ) res+=query(t[k].rs,mid+1,r,ql,qr);
28     return res;
29 }
30 std::unordered_map<int,HASH> map[maxn];
31 inline HASH Q ( int l,int len )
32 {
33     if ( map[l].count(len) ) return map[l][len];
34     return map[l][len]=query(root[l],1,n,l,l+len-1)*Pow[l];
35 }
36 inline int lcp ( int x,int y )
37 {
38     int l=0,r=n,ans=0,max=n-std::max(x,y)+1;
39     while ( l<=r )
40     {
41         int mid=(l+r)>>1;
42         if ( mid>max ) { r=mid-1;continue; }
43         if ( Q(x,mid)==Q(y,mid) ) l=mid+1,ans=mid;
44         else r=mid-1;
45     }
46     return ans;
47 }
48 int tmp[maxn];
49 inline bool cmp ( int x,int y )
50 {
51     int l=lcp(x,y);
52     if ( x+l>n ) return true;
53     if ( y+l>n ) return false;
54     return (*s[a[x+l]].lower_bound(x))-x<(*s[a[y+l]].lower_bound(y))-y;
55 }
56 inline void solve_sort ( int l,int r )
57 {
58     if ( l==r ) return;
59     int mid=(l+r)>>1;
60     solve_sort(l,mid);solve_sort(mid+1,r);
61     int pos=l-1;
62     for ( int pos1=l,pos2=mid+1;pos<r; )
63         if ( pos2==r+1 ) tmp[++pos]=p[pos1],pos1++;
64         else if ( pos1==mid+1 ) tmp[++pos]=p[pos2],pos2++;
65         else if ( !cmp(p[pos2],p[pos1]) ) tmp[++pos]=p[pos1],pos1++;
66         else tmp[++pos]=p[pos2],pos2++;
67     for ( int i=l;i<=r;i++ ) p[i]=tmp[i];
68 }
69 signed main()
70 {
71     Pow[0]=1;
72     for ( int i=1;i<=50000;i++ ) Pow[i]=Pow[i-1]*Hash;
73     while ( ~scanf("%d",&n) )
74     {
75         tot=root[n+1]=0;
76         for ( int i=1;i<=n;i++ ) s[a[i]=read()].insert(i),p[i]=i;
77         for ( int i=n;i;nxt[a[i]]=i,i-- )
78             if ( nxt[a[i]] ) modify(root[i],root[i+1],1,n,nxt[a[i]],(nxt[a[i]]-i)*Pow[n-i+1]);
79             else root[i]=root[i+1];
80         solve_sort(1,n);
81         long long ans=1LL*n*(n+1)/2;
82         for ( int i=1;i<n;i++ ) ans-=lcp(p[i],p[i+1]);
83         printf("%lld\n",ans);
84         for ( int i=1;i<=n;i++ ) map[i].clear(),s[i].clear(),nxt[i]=0;
85     }
86     return 0;
87 }
DTOJ4723/DTOJ4670

DTOJ #4720:区间

【题解】

考虑转化为字符串求解. 考虑本质不同的子串,建出 SAM,则每个节点的 $right$ 大小即为这个节点的贡献.

用单调栈维护编号,extend 时二分查找 $right$ 大小对应的编号位置,计算即可.

【代码】

 1 #include<bits/stdc++.h>
 2 inline int read ( void )
 3 {
 4     int x=0;char ch;
 5     while ( !isdigit(ch=getchar()) ) ;
 6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
 7     return x;
 8 }
 9 const int maxn=400000+10;
10 int size=1,root=1,last=1,val[maxn],len[maxn],fa[maxn],st[maxn],a[maxn],tp;
11 std::unordered_map<int,int> ch[maxn];long long sum[maxn],ans;
12 inline void extend ( int c )
13 {
14     int np=++size,p=last;val[np]=1;len[np]=len[p]+1;last=np;
15     while ( p and !ch[p].count(c) ) ch[p][c]=np,p=fa[p];
16     if ( !p ) fa[np]=root;
17     else
18     {
19         int q=ch[p][c];
20         if ( len[q]==len[p]+1 ) fa[np]=q;
21         else
22         {
23             int nq=++size;len[nq]=len[p]+1;fa[nq]=fa[q];
24             fa[q]=fa[np]=nq;ch[nq]=ch[q];
25             while ( p and ch[p][c]==q ) ch[p][c]=nq,p=fa[p];
26         }
27     }
28     int L=len[np]-len[fa[np]];
29     int pos=std::lower_bound(st+1,st+tp+1,L)-st;
30     ans+=sum[pos-1]+1LL*a[st[pos]]*(L-st[pos-1]);
31 }
32 inline void C ( void )
33 {
34     for ( int i=1;i<=size;i++ ) ch[i].clear(),fa[i]=0;
35     root=last=size=1;tp=0;ans=0;
36 }
37 signed main()
38 {
39     for ( int T=read();T--;C() )
40     {
41         int n=read();
42         for ( int i=1;i<=n;i++ )
43         {
44             a[i]=read();
45             while ( tp and a[st[tp]]<=a[i] ) tp--;
46             st[++tp]=i;sum[tp]=sum[tp-1]+1LL*a[i]*(st[tp]-st[tp-1]);
47             extend(a[i]);
48         }
49         printf("%lld\n",ans);
50     }
51     return 0;
52 }
DTOJ4720

DTOJ #4686:字符串

【题解】

$S,T$ 相似,等价于 $LCP(S,T)+LCS(S,T) \geq m-1$.

考虑建出原串的前后缀树,则要求为 $dep1[lca1(u,v)]+dep2[lca2(u,v)] \geq m-1$ 的对数.

在第一棵树上做 dsu on tree,用两个树状数组维护第二棵树的 dfs 序,一个更新一个计算答案即可.

效率 $O(n \log^2 n)$.

【代码】

  1 #include<bits/stdc++.h>
  2 const int maxn=400000+10;
  3 int n,m,ans[maxn];
  4 struct BIT
  5 {
  6     int n,t[maxn];
  7     inline void build ( int n ) { this->n=n; }
  8     inline void add ( int x,int y ) { for ( int i=x;i<=n;i+=i&(-i) ) t[i]+=y; }
  9     inline int query ( int x ) { int res=0;for ( int i=x;i;i-=i&(-i) ) res+=t[i];return res; }
 10 }T1,T2;
 11 struct Suffix_Auto_Machine
 12 {
 13     int size,root,last,fa[maxn],ch[maxn][27],len[maxn],map[maxn],Id[maxn];
 14     inline void build ( void ) { last=root=++size; }
 15     inline void extend ( int c,int id )
 16     {
 17         int np=++size,p=last;len[np]=len[p]+1;last=np;
 18         if ( id>0 ) Id[np]=id,map[id]=np;
 19         while ( p and !ch[p][c] ) ch[p][c]=np,p=fa[p];
 20         if ( !p ) { fa[np]=root;return; }
 21         int q=ch[p][c];
 22         if ( len[q]==len[p]+1 ) { fa[np]=q;return; }
 23         int nq=++size;len[nq]=len[p]+1;fa[nq]=fa[q];
 24         fa[q]=fa[np]=nq;memcpy(ch[nq],ch[q],sizeof(ch[nq]));
 25         while ( p and ch[p][c]==q ) ch[p][c]=nq,p=fa[p];
 26     }
 27 }A,B;
 28 struct TreeB
 29 {
 30     std::vector<int> e[maxn];
 31     int L[maxn],R[maxn],dfsn,p[maxn][22];
 32     inline void addedge ( int u,int v ) { e[u].push_back(v); }
 33     inline void dfs ( int u,int fr )
 34     {
 35         L[u]=++dfsn;p[u][0]=fr;
 36         for ( int i=1;p[u][i-1];i++ ) p[u][i]=p[p[u][i-1]][i-1];
 37         for ( int v:e[u] ) dfs(v,u);
 38         R[u]=dfsn;
 39     }
 40     inline int Up ( int x,int d )
 41     {
 42         if ( d<=0 ) return 1;
 43         for ( int j=20;~j;j-- ) if ( B.len[p[x][j]]>=d ) x=p[x][j];
 44         return x;
 45     }
 46 }TB;
 47 struct TreeA
 48 {
 49     int Id[maxn],siz[maxn],dep[maxn],son[maxn],val[maxn];
 50     std::vector<int> e[maxn];
 51     inline void addedge ( int u,int v ) { e[u].push_back(v); }
 52     inline void dfs ( int u,int fr )
 53     {
 54         siz[u]=1;dep[u]=A.len[u];Id[u]=A.Id[u];
 55         for ( int v:e[u] )
 56         {
 57             dfs(v,u);siz[u]+=siz[v];
 58             if ( siz[v]>siz[son[u]] ) son[u]=v;
 59         }
 60     }
 61     inline void calc ( int u )
 62     {
 63         if ( Id[u] )
 64         {
 65             int k=B.map[Id[u]];
 66             ans[Id[u]]+=T2.query(TB.L[k])-val[k];
 67         }
 68         for ( int v:e[u] ) calc(v);
 69     }
 70     inline void Del ( int u,int d )
 71     {
 72         if ( Id[u] )
 73         {
 74             int k=B.map[Id[u]];
 75             T1.add(TB.L[k],-1);val[k]=0;
 76             if ( B.len[k]>=m-1-d )
 77             {
 78                 int p=TB.Up(k,m-1-d);
 79                 T2.add(TB.L[p],-1);T2.add(TB.R[p]+1,1);
 80             }
 81         }
 82         for ( int v:e[u] ) Del(v,d);
 83     }
 84     inline void Ins ( int u,int d )
 85     {
 86         if ( Id[u] )
 87         {
 88             int k=B.map[Id[u]];
 89             T1.add(TB.L[k],1);
 90             if ( B.len[k]>=m-1-d )
 91             {
 92                 int p=TB.Up(k,m-1-d);
 93                 T2.add(TB.L[p],1);T2.add(TB.R[p]+1,-1);
 94                 val[k]=T2.query(TB.L[k]);
 95             }
 96         }
 97         for ( int v:e[u] ) Ins(v,d);
 98     }
 99     inline void ask ( int u,int d )
100     {
101         if ( Id[u] )
102         {
103             int k=B.map[Id[u]];
104             if ( B.len[k]>=m-1-d )
105             {
106                 int p=TB.Up(k,m-1-d);
107                 ans[Id[u]]+=T1.query(TB.R[p])-T1.query(TB.L[p]-1);
108             }
109         }
110         for ( int v:e[u] ) ask(v,d);
111     }
112     inline void getval ( int u,int d )
113     {
114         if ( Id[u] )
115         {
116             int k=B.map[Id[u]];
117             if ( B.len[k]>=m-1-d ) val[k]=T2.query(TB.L[k]);
118         }
119         for ( int v:e[u] ) getval(v,d);
120     }
121     inline void solve ( int u )
122     {
123         for ( int v:e[u] ) if ( v!=son[u] ) solve(v),calc(v),Del(v,dep[v]);
124         if ( son[u] ) solve(son[u]);
125         if ( Id[u] )
126         {
127             int k=B.map[Id[u]];
128             if ( B.len[k]>=m-1-dep[u] )
129             {
130                 int p=TB.Up(k,m-1-dep[u]);
131                 ans[Id[u]]+=T1.query(TB.R[p])-T1.query(TB.L[p]-1);
132                 T2.add(TB.L[p],1);T2.add(TB.R[p]+1,-1);
133                 val[k]=T2.query(TB.L[k]);
134             }
135             T1.add(TB.L[k],1);
136         }
137         for ( int v:e[u] ) if ( v!=son[u] ) ask(v,dep[u]),Ins(v,dep[u]),getval(v,dep[u]);
138     }
139 }TA;
140 char s[maxn];
141 signed main()
142 {
143     scanf("%d%d %s",&n,&m,s+1); 
144     A.build();B.build();
145     for ( int i=1;i<=n;i++ ) A.extend(s[i]^96,i-m+1);
146     for ( int i=n;i;i-- ) B.extend(s[i]^96,i);
147     for ( int i=B.size;i>1;i-- ) TB.addedge(B.fa[i],i);
148     TB.dfs(1,0);T1.build(B.size);T2.build(B.size);
149     for ( int i=A.size;i>1;i-- ) TA.addedge(A.fa[i],i);
150     TA.dfs(1,0);TA.solve(1);TA.calc(1);
151     for ( int i=1;i<=n-m+1;i++ ) printf("%d%c",ans[i]," \n"[i==n-m+1]);
152     return 0;
153 }
DTOJ4686

DTOJ #4680:黑红兔

【题解】

分析性质有:显然选出串的长度应为公差为 $-1$ 的等差数列,且最后一个数为 $1$.

以 $i$ 为开头的串,首串长为 $j$ 时能选出 $j$ 段,则首串长为 $j-1$ 时能选出 $j-1$ 段. 画图证明.

设 $f[i]$ 表示首串开头为 $i$ 最多能选出多少段.

二分 $mid$ 有,需要找到 $j \in [i+mid,n]$ 满足:$f[j]\geq mid-1, lcp(i,j) \geq mid-1 or lcp(i+1,j) \geq mid-1$.

显然后面那个限制条件可以用 SA+二分+RMQ 求出,前面一部分主席树维护即可. 效率 $O(n\log^2 n)$.

考虑优化,有:$f[i]\leq f[i+1]+1$. 抛弃二分,扫一遍即可. 效率 $O(n\log n)$.

【代码】

  1 #include<bits/stdc++.h>
  2 const int maxn=500000+10;
  3 int sa[maxn],rank[maxn],height[maxn],h[maxn],y[maxn],wr[maxn],rsort[maxn],n,m=26,ans;
  4 char s[maxn];int st[maxn][20],f[maxn],Pow[20],Log[maxn];
  5 inline void Get_SA ( void )
  6 {
  7     for ( int i=1;i<=n;i++ ) rank[i]=s[i]-96;
  8     for ( int i=1;i<=n;i++ ) rsort[rank[i]]++;
  9     for ( int i=1;i<=m;i++ ) rsort[i]+=rsort[i-1];
 10     for ( int i=n;i;i-- ) sa[rsort[rank[i]]--]=i;
 11     int lth=1,p=0;
 12     while ( p<n )
 13     {
 14         int k=0;
 15         for ( int i=n-lth+1;i<=n;i++ ) y[++k]=i;
 16         for ( int i=1;i<=n;i++ )
 17             if ( sa[i]>lth ) y[++k]=sa[i]-lth;
 18         for ( int i=1;i<=n;i++ ) wr[i]=rank[y[i]];
 19         memset(rsort,0,sizeof(rsort));
 20         for ( int i=1;i<=n;i++ ) rsort[wr[i]]++;
 21         for ( int i=1;i<=m;i++ ) rsort[i]+=rsort[i-1];
 22         for ( int i=n;i;i-- ) sa[rsort[wr[i]]--]=y[i];
 23         for ( int i=1;i<=n;i++ ) wr[i]=rank[i];
 24         p=1;rank[sa[1]]=1;
 25         for ( int i=2;i<=n;i++ )
 26         {
 27             if ( wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+lth]!=wr[sa[i-1]+lth] ) p++;
 28             rank[sa[i]]=p;
 29         }
 30         m=p;lth<<=1;
 31     }
 32     int k=0;
 33     for ( int i=1;i<=n;i++ )
 34     {
 35         int j=sa[rank[i]-1];
 36         if ( k ) k--;
 37         while ( s[j+k]==s[i+k] ) k++;
 38         height[rank[i]]=k;
 39     }
 40 }
 41 int root[maxn],tot;
 42 struct tree { int ls,rs,res; } t[20000000];
 43 inline void update ( int &k,int fr,int l,int r,int p,int v )
 44 {
 45     t[k=++tot]=t[fr];
 46     if ( l==r ) { t[k].res=std::max(t[k].res,v);return; }
 47     int mid=(l+r)>>1;
 48     if ( p<=mid ) update(t[k].ls,t[fr].ls,l,mid,p,v);
 49     else update(t[k].rs,t[fr].rs,mid+1,r,p,v);
 50     t[k].res=std::max(t[t[k].ls].res,t[t[k].rs].res);
 51 }
 52 inline int query ( int k,int l,int r,int ql,int qr )
 53 {
 54     if ( !k or ( ql<=l and r<=qr ) ) return t[k].res;
 55     int mid=(l+r)>>1,res=0;
 56     if ( ql<=mid ) res=std::max(res,query(t[k].ls,l,mid,ql,qr));
 57     if ( qr>mid ) res=std::max(res,query(t[k].rs,mid+1,r,ql,qr));
 58     return res;
 59 }
 60 inline int rmq ( int l,int r )
 61 {
 62     int k=Log[r-l+1];
 63     return std::min(st[l][k],st[r-Pow[k]+1][k]);
 64 }
 65 inline int erfen1 ( int p,int len )
 66 {
 67     int l=1,r=p-1;
 68     while ( l<=r )
 69     {
 70         int mid=(l+r)>>1;
 71         if ( rmq(mid+1,p)>=len-1 ) r=mid-1;
 72         else l=mid+1;
 73     }
 74     return l;
 75 }
 76 inline int erfen2 ( int p,int len )
 77 {
 78     int l=p+1,r=n;
 79     while ( l<=r )
 80     {
 81         int mid=(l+r)>>1;
 82         if ( rmq(p+1,mid)>=len-1 ) l=mid+1;
 83         else r=mid-1;
 84     }
 85     return r;
 86 }
 87 inline bool check ( int p )
 88 {
 89     if ( query(root[p+f[p]],1,n,erfen1(rank[p],f[p]),erfen2(rank[p],f[p]))>=f[p]-1 ) return true;
 90     if ( query(root[p+f[p]],1,n,erfen1(rank[p+1],f[p]),erfen2(rank[p+1],f[p]))>=f[p]-1 ) return true;
 91     return false;
 92 }
 93 int main()
 94 {
 95     scanf("%s",s+1);n=strlen(s+1);Get_SA();Pow[0]=1;
 96     for ( int i=2;i<=n;i++ ) Log[i]=Log[i>>1]+1;
 97     for ( int i=1;i<=Log[n];i++ ) Pow[i]=Pow[i-1]<<1;
 98     for ( int i=2;i<=n;i++ ) st[i][0]=height[i];
 99     for ( int j=1;j<=Log[n];j++ ) for ( int i=1;i+Pow[j]-1<=n;i++ ) st[i][j]=std::min(st[i][j-1],st[i+Pow[j-1]][j-1]);
100     ans=f[n]=1;update(root[n],0,1,n,rank[n],1);
101     for ( int i=n-1;i;i-- )
102     {
103         f[i]=f[i+1]+1;
104         while ( !check(i) ) f[i]--;
105         update(root[i],root[i+1],1,n,rank[i],f[i]);
106         ans=std::max(ans,f[i]);
107     }
108     return !printf("%d\n",ans);
109 }
DTOJ4680

DTOJ #4731:回文

【题解】

PAM裸题. 考虑回文串的一个回文子串,分两类讨论,一类包含最后一个字符即母串后缀,跳回文树即可. 另一类删去首尾两个字符,沿 PAM dfs 一遍即可.

跳回文树时记得打上标记,使子树内节点不再访问,同时保证时间复杂度. 效率 $O(\sum n)$. 期望得分:100.

【代码】

 1 #include<bits/stdc++.h>
 2 const int maxn=5000000+10;
 3 char s[maxn];bool vis[maxn];
 4 int T,n,len[maxn],fail[maxn],ch[maxn][26],cnt,last;
 5 long long Ans,ans[maxn];
 6 inline void dfs ( int u,int fr )
 7 {
 8     int dep=0,x=u;vis[x]=true;
 9     while ( fail[x]>1 and !vis[fail[x]] ) x=fail[x],vis[x]=true,dep++;
10     ans[u]=dep+((fr>1)?(ans[fr]+1):0);Ans+=ans[u];
11     for ( int i=0;i<26;i++ ) if ( ch[u][i] ) dfs(ch[u][i],u);
12     x=u;vis[x]=false;
13     while ( dep-- ) x=fail[x],vis[x]=false;
14 }
15 int main()
16 {
17     for ( scanf("%d",&T);T--; )
18     {
19         scanf(" %s",s+1);n=strlen(s+1);Ans=0;
20         last=cnt=0;s[0]='#';fail[0]=1;len[++cnt]=-1;
21         for ( int i=1;i<=n;i++ )
22         {
23             while ( s[i-len[last]-1]!=s[i] ) last=fail[last];
24             if ( !ch[last][s[i]-97] )
25             {
26                 len[++cnt]=len[last]+2;
27                 int j=fail[last];
28                 while ( s[i-len[j]-1]!=s[i] ) j=fail[j];
29                 fail[cnt]=ch[j][s[i]-97];ch[last][s[i]-97]=cnt;
30             }
31             last=ch[last][s[i]-97];
32         }
33         dfs(0,0);dfs(1,1);printf("%lld\n",Ans);
34         for ( int i=0;i<=cnt;i++ )
35         {
36             for ( int j=0;j<26;j++ ) ch[i][j]=0;
37             fail[i]=ans[i]=len[i]=0;
38         }
39     }
40     return 0;
41 }
DTOJ4731

DTOJ #1265:双倍回文

【题解】

上题的双倍经验双倍难度. 同理考虑回文后缀,显然需要找到长度小于等于一半的回文后缀,记作 $trans$,像 $fail$ 一样跳即可.

显然若一个节点的 $trans$ 刚好为其长度的一半,则这个点可以作为答案.

【代码】

 1 #include<bits/stdc++.h>
 2 const int maxn=5000000+10;
 3 char s[maxn];bool vis[maxn];
 4 int T,n,len[maxn],fail[maxn],trans[maxn],ch[maxn][26],cnt,last;
 5 inline int getfail ( int x,int n ) { while ( s[n-len[x]-1]!=s[n] ) x=fail[x]; return x; }
 6 inline int gettrans ( int x,int n,int now ) { while ( s[n-len[x]-1]!=s[n] or (len[x]+2)*2>len[now] ) x=fail[x]; return x; }
 7 int main()
 8 { 
 9     scanf("%d %s",&n,s+1);
10     s[0]='#';fail[0]=1;len[++cnt]=-1;
11     for ( int i=1;i<=n;i++ )
12     {
13         last=getfail(last,i);
14         if ( !ch[last][s[i]-97] )
15         {
16             len[++cnt]=len[last]+2;
17             fail[cnt]=ch[getfail(fail[last],i)][s[i]-97];
18             ch[last][s[i]-97]=cnt;
19             if ( len[cnt]<=2 ) trans[cnt]=fail[cnt];
20             else trans[cnt]=ch[gettrans(trans[last],i,cnt)][s[i]-97];
21         }
22         last=ch[last][s[i]-97];
23     }
24     int ans=-(1<<30);
25     for ( int i=2;i<=cnt;i++ ) if ( len[trans[i]]*2==len[i] and !(len[i]%4) ) ans=std::max(ans,len[i]);
26     return !printf("%d\n",ans);
27 }
DTOJ1265

 

posted @ 2020-02-24 17:14  DTOI_RSY  阅读(228)  评论(0编辑  收藏  举报