LOJ #6158. A + B Problem
给出一个数字,求在其中两位之间插入一个加号后得到的答案末尾 最多的个数。
题解:
第二个数的末尾就是原串的末尾,所以如果需要和第一个数加起来末尾为
,那么从后往前扫,第二个数为
的位在不进位的情况下第一个数的对应位需要是
,不为
的位的对应位
应该是
,然后开启进位模式所有数的对应位都是
,然后求一个原串和转换后的串的最长公共前缀即可得出答案,可以写
#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 998244353
#define S 131
#define LL long long
using namespace std;
char s[maxn],s0[maxn];
LL hs[2][maxn],pw[maxn];
int n;
LL calc(int a,int b,LL *hs){ return ((hs[b] - hs[a-1] * pw[b-a+1]) % mod + mod) % mod; }
int main(){
pw[0] = 1;
rep(i,1,maxn-1) pw[i] = pw[i-1] * S % mod;
while(scanf("%s",s+1)!=EOF){
n = strlen(s+1);
rep(i,1,n) hs[0][i] = (hs[0][i-1] * S + s[i]) % mod , s0[i] = s[i];
int i;
for(i=n;i>=1 && s[i] == '0';i--);
s[i] = (10 - (s[i] - '0')) + '0';
int t = i;
per(j,i-1,1) s[j] = (9 - (s[j] - '0')) + '0';
rep(i,1,n) hs[1][i] = (hs[1][i-1] * S + s[i]) % mod;
int ans = 0;
rep(i,1,n-1){
int L = 0 , R = min(i , n-i) , mid;
for(;L<R;){
mid = L+R+1 >> 1;
if(calc(i-mid+1,i,hs[0]) == calc(n-mid+1,n,hs[1]))
L = mid;
else
R = mid - 1;
}
if(i <= t){
while(n-L <= i && s0[i-L] == '9') L++;
while(i-L <= 0 && n-L > i && s0[n-L] == '9') L++;
}
else{
while(n-L <= i && s0[i-L] == '0') L++;
while(i-L <= 0 && n-L > i &&s0[n-L] == '0') L++;
}
ans = max(ans , L);
}
printf("%d\n",ans);
}
}
CF914F Substrings in a String
题意:单点修改 中的一个字符,求 中 的出现次数,
太离谱了。
考虑到这是
的
,我们用
代表字符
在位置
是否出现。
则求出
后取
在
中的
的个数即可。
时间复杂度
无压力水过
其实可以分块一下得到更科学的复杂度:
对于长度大于块大小
的字符串我们暴力
,
对于长度小于块大小
的字符串我们分它是在一个块内还是跨两个块来考虑。
对于块内的答案直接分块求块内的
即可做到
对于跨两个块的答案直接对于
个交界处前后
个字符拿出来跑
,
,总复杂度还是
所以
时最快,为
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
char s[maxn],ch[maxn];
int Q,n;
bitset<maxn>C[26];
int main(){
scanf("%s",s+1);
n = strlen(s+1);
rep(i,1,n) C[s[i] - 'a'][i] = 1;
scanf("%d",&Q);
for(int op,l,r;Q--;){
scanf("%d%d",&op,&l);
if(op == 1){
scanf("%s",ch);
C[s[l]-'a'][l] = 0;
C[(s[l]=ch[0])-'a'][l] = 1;
}
else{
scanf("%d%s",&r,ch);
int t = strlen(ch);
static bitset<maxn>ans;
ans.set();
rep(i,0,t-1)
ans &= (C[ch[i] - 'a'] >> i);
r -= t - 1;
ans <<= maxn - 1 - r;
ans >>= maxn - 1 - r + l;
printf("%d\n",ans.count());
}
}
}
做法:(实际上没有快多少)
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define S 1800
using namespace std;
char s[maxn],ch[maxn];
int Q,n,id[maxn],st[maxn],ed[maxn],nxt[maxn],m;
bitset<S>C[26][maxn/S+5];
int kmp(int l,int r){
int ans = 0;
for(int i=l,j=0;i<=r;i++){
while(j != -1 && ch[j] != s[i]) j = nxt[j];
if(++j == m) ans ++;
}
return ans;
}
int solve(int b,int l,int r){
static bitset<S>ans;ans.set();
rep(i,0,m-1) ans &= C[ch[i]-'a'][b] >> i;
r -= m - 1;
ans <<= S - 1 - r;
ans >>= S - 1 - r + l;
return ans.count();
}
int main(){
scanf("%s",s+1);
n = strlen(s+1);
rep(i,1,n) id[i] = i / S , st[id[i]] = st[id[i]] ? st[id[i]] : i , ed[id[i]] = i;
rep(i,1,n) C[s[i]-'a'][id[i]][i - st[id[i]]] = 1;
scanf("%d",&Q);
for(int op,l,r;Q--;){
scanf("%d%d",&op,&l);
if(op == 1){
scanf("%s",ch);
C[s[l]-'a'][id[l]][l - st[id[l]]] = 0;
C[(s[l]=ch[0])-'a'][id[l]][l - st[id[l]]] = 1;
}
else{
scanf("%d%s",&r,ch);
m = strlen(ch);
nxt[0] = -1;
for(int j=-1,k=0;k<m;)
if(j == -1 || ch[j] == ch[k]) nxt[++k] = ++j;
else j = nxt[j];
int ans = 0;
if(m >= S) ans = kmp(l,r);
else{
rep(i,id[l],id[r]) ans += solve(i,max(l-st[i],0),min(r-st[i],ed[i]-st[i]));
rep(i,id[l],id[r]-1) ans += kmp(max(l,ed[i]-m+2),min(ed[i]+m-1,r));
}
printf("%d\n",ans);
}
}
}
UPD:完全错了,后一种做法还是 的,根号做法还是推荐分块维护后缀自动机加 。
CF1131E String Multiplication
在加入
的时候随便维护一下每个字母的最长相同字母连续子序列即可。
#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
char s[maxn];
int n,m;
LL f[26];
int main(){
scanf("%d",&n);
for(;n--;){
scanf("%s",s);
m = strlen(s);
vector<pair<int,int> >G;
for(int i=0,j;i<m;i=j){
for(j=i;j < m && s[i] == s[j];j++);
G.push_back(make_pair(s[i]-'a' , j-i));
}
if(G.size() == 1){
rep(i,0,25) if(i ^ G[0].first)
f[i] = min(f[i] , 1ll);
f[G[0].first] = min(f[G[0].first] + (f[G[0].first] + 1ll) * G[0].second , (LL)1e11);
}
else{
rep(i,0,25) f[i] = min(f[i] , 1ll);
if(G[0].first == G.back().first) f[G[0].first] += G[0].second + G.back().second;
else{
f[G[0].first] += G[0].second;
f[G.back().first] += G.back().second;
}
rep(i,0,G.size()-1) f[G[i].first] = max(f[G[i].first] , G[i].second * 1ll);
}
}
LL ans = 0;
rep(i,0,25) ans = max(ans , f[i]);
printf("%d\n",ans);
}
CF653F Paper task
给定一个长度为 的括号串,问有多少种不同的合法的本质不同的括号子串。
一边插入字符建出 ,一边对于新出现的节点套个 统计新节点所代表的串中有几个合法的括号串。
#include<bits/stdc++.h>
#define maxn 500005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
int n;
char s[maxn];
map<int,int>P[maxn];
int fa[maxn<<1],last,len[maxn<<1],tr[maxn<<1][2],tot;
void ins(int c){
int u = ++tot , p = last , q;
len[last = u] = len[p] + 1;
for(;p!=-1 && !tr[p][c];p=fa[p]) tr[p][c] = u;
if(p == -1) fa[u] = 0;
else if(len[q = tr[p][c]] == len[p] + 1) fa[u] = q;
else{
int v = ++tot;
memcpy(tr[v],tr[q],sizeof tr[q]),fa[v]=fa[q],len[v]=len[p]+1;
for(;p!=-1 && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = fa[u] = v;
}
}
int main(){
scanf("%d%s",&n,s+1);
rep(i,0,n) P[i][0] = 0;
stack<int>sta;
LL ans = 0;fa[0] = -1;
rep(i,1,n){
ins(s[i] == '(' ? 0 : 1);
if(s[i] == '(')
sta.push(i);
else{
if(sta.empty()) P[0].clear(),P[0][0] = 0;
else{
int u = sta.top() , pr;
sta.pop();
if(sta.empty()) pr = 0;
else pr = sta.top();
int t = (*P[pr].rbegin()).second + 1;
P[pr][u] = t;
map<int,int>::iterator it = P[pr].lower_bound(i - len[fa[last]] + 1);
it--;
ans += (*it).second;
}
}
}
printf("%lld\n",ans);
}
CF610E Alphabet Permutations
字符集为前 个小写字母,给出一个长度为 的字符串 ,有 次操作,有两种操作,一种是将 赋值为字符 ,一种是给出 个小写字母的一个排列作为字符串 ,询问往 中插入字符后成为 循环 次后,求最小的 。
水题,线段树随便维护一下连续两个字符为 的方案数,那么对于给出的排列, 的位置 的位置则答案增加 的方案数。
#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define lc u<<1
#define rc lc|1
using namespace std;
int n,m,K;
int tr[maxn<<2][10][10],tag[maxn<<2],len[maxn<<2],pl[maxn<<2],pr[maxn<<2];
char ch[maxn],s[maxn];
void dtp(int u,int p){
memset(tr[u],0,sizeof tr[u]);
tr[u][p][p] = len[u]-1;
pl[u] = pr[u] = p;
tag[u] = p;
}
void dt(int u){
if(tag[u] != -1){
dtp(lc,tag[u]);
dtp(rc,tag[u]);
tag[u] = -1;
}
}
void upd(int u){
rep(i,0,K-1) rep(j,0,K-1) tr[u][i][j] = tr[lc][i][j] + tr[rc][i][j];
tr[u][pr[lc]][pl[rc]] ++;
pl[u] = pl[lc] , pr[u] = pr[rc];
}
void Build(int u,int l,int r){
len[u] = r-l+1;
if(l==r) return (void)(pl[u]=pr[u]=s[l]-'a');
int m=l+r>>1;
Build(lc,l,m),Build(rc,m+1,r);
upd(u);
}
void ins(int u,int l,int r,int ql,int qr,int p){
if(l>qr||ql>r) return ;
if(ql<=l&&r<=qr) return (void)(dtp(u,p));
int m=l+r>>1;dt(u);
ins(lc,l,m,ql,qr,p) , ins(rc,m+1,r,ql,qr,p);
upd(u);
}
int main(){
memset(tag,-1,sizeof tag);
scanf("%d%d%d",&n,&m,&K);
scanf("%s",s+1);
Build(1,1,n);
for(int op,l,r,c;m--;){
scanf("%d",&op);
if(op == 1){
scanf("%d%d%s",&l,&r,&ch);
ins(1,1,n,l,r,ch[0]-'a');
}
else{
scanf("%s",ch);
static int p[11]={};
int ans = 1;
rep(i,0,K-1) rep(j,0,i)
ans += tr[1][ch[i]-'a'][ch[j]-'a'];
printf("%d\n",ans);
}
}
}
CF741E Arpa’s abnormal DNA and Mehrdad’s deep interest
写
实现
后缀排序后就是一个求
的
问题。
对于这
,
我们分
,则离线后处理
相等的所有询问,对于
相同的所有
写一个
。
总复杂度
,则暴力找所有的
的区间,复杂度
。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define pb push_back
#define Ct const
#define mod 998244353
using namespace std;
char s[maxn],t[maxn];
int q,ls,lt,c[maxn],rk[maxn];
LL pw[maxn],hst[maxn],hs[maxn];
LL calc(int u,int l){
if(l <= u) return hs[l];
if(l <= u + lt) return (hs[u] * pw[l-u] + hst[l-u]) % mod;
return ((hs[u] * pw[l-u] + hst[lt] * pw[l-u-lt] + hs[l-lt] - hs[u] * pw[l-lt-u]) % mod + mod) % mod;
}
int getc(int u,int l){
if(l <= u) return s[l];
if(l <= u + lt) return t[l-u];
return s[l-lt];
}
bool cmp(Ct int &u,Ct int &v){
int L = 0 , R = ls + lt , mid;
for(;L<R;){
mid = L+R+1 >> 1;
if(calc(u,mid) == calc(v,mid)) L = mid;
else R = mid - 1;
}
return getc(u,L+1) < getc(v,L+1);
}
#define vc vector
#define vi vector<int>
#define lim 17
#define S 100
int L[maxn],R[maxn],K[maxn],X[maxn],Y[maxn],lg[maxn],ans[maxn];
vi G[maxn];
vc<vi>st;
bool cmp2(Ct int &u,Ct int &v){ if(rk[u] < rk[v] || (rk[u] == rk[v] && u < v)) return 1;return 0; }
int qry(int u,int v){
if(u > v) return ls+1;
int t = lg[v-u+1];
return cmp2(st[t][u],st[t][v-(1<<t)+1]) ? st[t][u] : st[t][v-(1<<t)+1];
}
int main(){
scanf("%s%s%d",s+1,t+1,&q);
ls = strlen(s+1);
lt = strlen(t+1);
rep(i,1,ls) hs[i] = (hs[i-1] * S + s[i]) % mod;
rep(i,1,lt) hst[i] = (hst[i-1] * S + t[i]) % mod;
pw[0] = 1;
rep(i,1,ls+lt) pw[i] = pw[i-1] * S % mod;
rep(i,0,ls) c[i] = i;
sort(c,c+ls+1,cmp);
int cnt = 0;
rep(i,0,ls)
if(i == 0 || cmp(c[i-1],c[i]))
rk[c[i]] = ++cnt;
else rk[c[i]] = cnt;
st=vc<vi>(lim,vi(ls+1));
rep(i,0,ls) st[0][i] = i;
rep(j,1,lim-1) rep(i,0,ls-(1<<j)+1)
st[j][i] = cmp2(st[j-1][i],st[j-1][i+(1<<j-1)]) ? st[j-1][i] : st[j-1][i+(1<<j-1)];
rep(i,2,ls) lg[i] = lg[i>>1] + 1;
rk[ls+1] = 0x3f3f3f3f;
rep(i,1,q){
scanf("%d%d%d%d%d",&L[i],&R[i],&K[i],&X[i],&Y[i]);
ans[i] = ls+1;
if(K[i] <= S) G[K[i]].pb(i);
else{
for(int j=0;j*K[i]+X[i]<=ls;j++)
if(j*K[i]+X[i] <= R[i] && j*K[i]+Y[i] >= L[i]){
int t = qry(max(j*K[i]+X[i],L[i]),min(j*K[i]+Y[i],R[i]));
if(cmp2(t,ans[i])) ans[i] = t;
}
}
}
rep(i,1,S) rep(j,0,i-1){
st=vc<vi>(lim,vi(ls / i + 5));
int len;
for(len=0;i*len+j<=ls;len++) st[0][len] = i*len+j;
rep(k,1,lim-1) rep(p,0,len-(1<<k)+1)
st[k][p] = cmp2(st[k-1][p],st[k-1][p+(1<<k-1)]) ? st[k-1][p] : st[k-1][p+(1<<k-1)];
for(int v:G[i]) if(X[v] <= j && j <= Y[v]){
int t = qry((int)ceil((1.0 * L[v]-j) / i) , (int)floor((1.0 * R[v] - j) / i));
if(cmp2(t , ans[v]))
ans[v] = t;
}
}
rep(i,1,q) if(ans[i] == ls+1) ans[i] = -1;
rep(i,1,q)
printf("%d%c",ans[i]," \n"[i==q]);
}
CF862F Mahmoud and Ehab and the final stage
有 个字符串 和 次操作,每次操作是 表示询问区间 的所有子区间 中, 的最大值。 表示把第 个字符串改成 。 。
容易发现
和
其中必有一个不会大于
,因为如果
,则
的长度不可能都
,所以
。
所以就维护相邻两个字符串的
数组,每次找出
的位置暴力插入维护答案,同时对于
每个维护一个线段树维护区间最长为
的连续子段即可。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define pb push_back
#define S 350
#define maxp maxn * 100
using namespace std;
int n,Q,a[maxn];
string s[maxn];
int LCP(const string &a,const string &b){
int r=0;
for(;r < a.size() && r < b.size() && a[r] == b[r];r++);
return r;
}
int lc[maxp],rc[maxp],lx[maxp],rx[maxp],mx[maxp],sz[maxp],bin[maxp],tot,rt[maxn];
int newnode(){
int r = 0;
if(bin[0]){
r = bin[bin[0] --];
lc[r] = rc[r] = lx[r] = rx[r] = mx[r] = sz[r] = 0;
}
else r = ++tot;
return r;
}
void upd(int u,int l,int m,int r){
sz[u] = sz[lc[u]] + sz[rc[u]];
lx[u] = (lx[lc[u]] == m-l+1 ? lx[rc[u]] : 0) + lx[lc[u]];
rx[u] = (rx[rc[u]] == r-m ? rx[lc[u]] : 0) + rx[rc[u]];
mx[u] = max(mx[lc[u]] , max(mx[rc[u]] , lx[rc[u]] + rx[lc[u]]));
}
void ins(int &u,int l,int r,int p,int v){
if(!u) u = newnode();
sz[u] += v;
if(l == r){
lx[u] = rx[u] = mx[u] = sz[u];
if(!sz[u]) bin[++bin[0]] = u , u = 0;
return;
}
int m = l+r>>1;
p <= m ? ins(lc[u],l,m,p,v) : ins(rc[u],m+1,r,p,v);
upd(u,l,m,r);
if(!sz[u]) bin[++bin[0]] = u , u = 0;
}
void qry(int u,int l,int r,int ql,int qr,int &MX,int &RX){
if(ql>r||l>qr||!u) return (void)(RX = 0);
if(ql<=l&&r<=qr) return (void)( MX = max(MX , max(mx[u] , RX + lx[u])) , RX = (rx[u] == r-l+1 ? RX : 0) + rx[u] );
int m = l+r>>1;
qry(lc[u],l,m,ql,qr,MX,RX) , qry(rc[u],m+1,r,ql,qr,MX,RX);
}
int c[maxn];
bool cmp(const int &u,const int &v){ return a[u] > a[v]; }
int F[maxn],SZ[maxn];
int Find(int u){ return !F[u] ? u : F[u] = Find(F[u]); }
set<int>mS,mA;
namespace SGT{
int mx[maxn<<2];
#define lc u<<1
#define rc lc|1
void ins(int u,int l,int r,int p,int v){
if(l == r) return (void)(mx[u] = v);
int m = l+r>>1;
p <= m ? ins(lc,l,m,p,v) : ins(rc,m+1,r,p,v);
mx[u] = max(mx[lc] ,mx[rc]);
}
int qry(int u,int l,int r,int ql,int qr){
if(l>qr||ql>r) return 0;
if(ql<=l&&r<=qr) return mx[u];
int m = l+r>>1;
return max(qry(lc,l,m,ql,qr) , qry(rc,m+1,r,ql,qr));
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> Q;
rep(i,1,n){
cin >> s[i];
SGT::ins(1,1,n,i,s[i].length());
if(i > 1){
a[i-1] = LCP(s[i-1],s[i]);
if(a[i-1] >= S) mA.insert(i-1);
rep(j,1, min(a[i-1],S))
ins(rt[j],1,n-1,i-1,1);
}
}
for(int op,l,r;Q--;){
cin >> op >> l;
if(op == 1) {
cin >> r;
LL ans = SGT::qry(1,1,n,l,r);
c[0] = 0;
for(int v:mA) if(l <= v && v < r) c[++c[0]] = v , F[v] = F[v+1] = 0 , SZ[v] = SZ[v+1] = 1;
sort(c+1,c+c[0]+1,cmp);
rep(i,1,c[0]){
int u = c[i] , v = u + 1;
F[v] = Find(u);
SZ[Find(u)] += SZ[v];
ans = max(ans , 1ll * SZ[Find(u)] * a[u]);
}
rep(i,1,S){
int MX = 0 , RX = 0;
qry(rt[i],1,n-1,l,r-1,MX,RX);
if(MX) ans = max(ans , 1ll * (MX+1) * i);
}
printf("%lld\n",ans);
}
else{
if(l > 1){
if(a[l-1] >= S) mA.erase(l-1);
rep(j,1,min(a[l-1],S))
ins(rt[j],1,n-1,l-1,-1);
}
if(l < n){
if(a[l] >= S) mA.erase(l);
rep(j,1,min(a[l],S))
ins(rt[j],1,n-1,l,-1);
}
cin >> s[l];
SGT::ins(1,1,n,l,s[l].length());
if(l > 1){
a[l-1] = LCP(s[l-1],s[l]);
if(a[l-1] >= S) mA.insert(l-1);
rep(j,1, min(a[l-1],S))
ins(rt[j],1,n-1,l-1,1);
}
if(l < n){
a[l] = LCP(s[l],s[l+1]);
if(a[l] >= S) mA.insert(l);
rep(j,1,min(a[l],S))
ins(rt[j],1,n-1,l,1);
}
}
}
}
CF587F Duff is Mad
还是分块,我吐了。
对于长度
的建出
的
自动机后暴力查询
的子树和。
对于长度
的离线在
自动机从
加到
,然后
在
自动机上查询的父亲和,注意修改数是
,查询数是
,可以用分块维护前缀和得到
的复杂度。
#include<bits/stdc++.h>
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define pb push_back
#define LL long long
#define S 305
using namespace std;
int n,Q;
string s[maxn];
int L[maxn],R[maxn],K[maxn];
LL ans[maxn];
vector<int>in[maxn],ot[maxn],G[maxn<<1];
#define maxc 26
LL sm[maxn];
int last,fa[maxn<<1],tr[maxn<<1][maxc],len[maxn<<1],tot,f[maxn<<1],sa[maxn<<1],c[maxn<<1],pos[maxn];
void ins(int c){
if(tr[last][c]){
int p = last , q;
if(len[q = tr[p][c]] != len[p] + 1){
int v = ++tot;
memcpy(tr[v],tr[q],sizeof tr[q]),fa[v] =fa[q] , len[v] = len[p] + 1;
for(;p != -1 && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = v;
}
last = tr[last][c];
return;
}
int u = ++tot , p = last , q;
len[last = u] = len[p] + 1;
for(;p!=-1 && tr[p][c]==0;p=fa[p]) tr[p][c]=u;
if(p == -1) fa[u] = 0;
else if(len[q=tr[p][c]] == len[p] + 1) fa[u] = q;
else{
int v = ++tot;
memcpy(tr[v],tr[q],sizeof tr[q]),fa[v] =fa[q] , len[v] = len[p] + 1;
for(;p != -1 && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = fa[u] = v;
}
}
int st[maxn<<1],ed[maxn<<1],tim,ST[maxn<<1],ED[maxn<<1];
void dfs0(int u){
st[u] = ++tim;
for(int v:G[u])
dfs0(v);
ed[u] = tim;
}
int bl[maxn],sb[maxn<<1],id[maxn<<1];
int main(){
ios::sync_with_stdio(false);
cin >> n >> Q;
rep(i,1,n) cin>>s[i];
rep(i,1,Q){
cin >> L[i] >> R[i] >> K[i];
if(s[K[i]].length() <= S) ot[R[i]].pb(i),in[L[i]-1].pb(i);
else G[K[i]].pb(i);
}
rep(i,1,n) if(!G[i].empty()){
last = 0 , fa[0] = -1;
rep(j,0,s[i].length()-1) ins(s[i][j]-'a'),f[last]++;
rep(j,0,tot) c[j] = 0;
rep(j,0,tot) c[len[j]]++;
rep(j,1,tot) c[j] += c[j-1];
rep(j,0,tot) sa[--c[len[j]]] = j;
per(j,tot,1){
int u = sa[j];
f[fa[u]] += f[u];
}
rep(j,1,n){
int u = 0 , L = 0;
rep(k,0,s[j].length()-1){
int v = s[j][k] - 'a';
for(;u != -1 && !tr[u][v];u=fa[u]);
if(u == -1) u = 0 , L = 0;
else L = min(L + 1 , len[u] + 1) , u = tr[u][v];
}
sm[j] = sm[j-1] + (L == s[j].length()) * (f[u]);
}
for(int v:G[i])
ans[v] = sm[R[v]] - sm[L[v]-1];
G[i].clear();
memset(tr,0,sizeof (tr[0]) * (tot+1));
rep(j,0,tot) f[j] = 0;
tot = 0;
}
last = 0;fa[0] = -1;
rep(i,1,n){
last = 0;
rep(j,0,s[i].length()-1) ins(s[i][j]-'a');
pos[i] = last;
}
rep(i,1,tot) G[fa[i]].pb(i);
dfs0(0);
rep(i,1,tim) id[i] = i / S , ST[id[i]] = ST[id[i]] ? ST[id[i]] : i , ED[id[i]] = i;;
rep(i,1,n){
int u = st[pos[i]] , v = ed[pos[i]];
rep(j,id[u]+1,id[v]-1) bl[j] ++;
if(id[u] == id[v]){
rep(j,u,v) sb[j]++;
}
else{
rep(j,u,ED[id[u]]) sb[j]++;
rep(j,ST[id[v]],v) sb[j]++;
}
for(int p:in[i]){
int u = 0 , t = K[p];
rep(j,0,s[t].length()-1){
v = s[t][j] - 'a';
for(;u != -1 && !tr[u][v];u = fa[u]);
if(u == -1) u = 0;
else u = tr[u][v];
ans[p] -= sb[st[u]] + bl[id[st[u]]];
}
}
for(int p:ot[i]){
int u = 0 , t = K[p];
rep(j,0,s[t].length()-1){
v = s[t][j] - 'a';
for(;u != -1 && !tr[u][v];u = fa[u]);
if(u == -1) u = 0;
else u = tr[u][v];
ans[p] += sb[st[u]] + bl[id[st[u]]];
}
}
}
rep(i,1,Q) printf("%lld\n",ans[i]);
}
CF1110H Modest Substrings
LOJ #6681. yww 与树上的回文串
给一棵树,每条边上有一个字符,求有多少对 ,满足 到 路径上的边上的字符按顺序组成的字符串为回文串。
题解:
点分治,然后回文串分为两部分
和
,其中
是
的一个回文前缀,所以我们建出
自动机,同时用
判断每个前缀是否是回文前缀。
算了太毒了。