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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }