[zr省选十连测day4]逆转函数_题解【回文树】
[zr省选十连测day4]逆转函数_题解
官方题解没看懂,所以这里来说一说我的思路。
记 l i l_i li 表示 i i i 左边第一个和它相等的数的位置,没有就等于 0 0 0。 r i r_i ri 表示 i i i 右边第一个与它相等的数的位置,没有就等于 n + 1 n+1 n+1 。
考虑一个合法区间需要满足什么,现在有一个区间 [ x , y ] [x,y] [x,y] ,它合法的条件是
- [ x + 1 , y − 1 ] [x+1,y-1] [x+1,y−1] 是合法区间
- 下面两个条件要满足一个
- ∣ x − r x ∣ = ∣ y − l y ∣ \left|x-r_x\right|=\left|y-l_y\right| ∣x−rx∣=∣y−ly∣
- r x > y , l y < x r_x > y,l_y < x rx>y,ly<x
我们分析可以得出一个性质,如果 [ x , y ] [x,y] [x,y] 和 [ a , b ] ( x ≤ a , b ≤ y ) [a,b](x\le a,b\le y) [a,b](x≤a,b≤y) 都是合法区间,那么 [ x + y − b , x + y − a ] [x+y-b,x+y-a] [x+y−b,x+y−a] 也是合法区间。(这个分析不难,但是我懒得在这里画图,有疑惑欢迎来问我。
知道了这个性质,我们就可以用马拉车了。但是你发现这个答案的贡献特别恶心,区间 [ x , y ] [x,y] [x,y] 的贡献是 m [ x , y ] 中不同的数的个数 m^{[x,y]\text{中不同的数的个数}} m[x,y]中不同的数的个数
这就意味着无法直接继承对称点的答案。
记点 i i i 从的对称点为 f a [ i ] fa[i] fa[i] ,记点 i i i 从对称点继承过来的回文长度(指真实回文串长度的一半)为 s [ i ] s[i] s[i],它最长的回文长度为 l e n [ i ] len[i] len[i] 。我们以 i , f a [ i ] i,fa[i] i,fa[i] 的关系来建一颗树。
对每个点
i
i
i 维护
f
[
i
]
[
j
]
(
0
≤
j
≤
l
e
n
[
i
]
−
s
[
i
]
)
f[i][j](0\le j\le len[i]-s[i])
f[i][j](0≤j≤len[i]−s[i]) ,
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示以
i
i
i 为中心回文长度为
s
[
i
]
+
0
,
s
[
i
]
+
1
,
.
.
.
s
[
i
]
+
j
s[i]+0,s[i]+1,...s[i]+j
s[i]+0,s[i]+1,...s[i]+j 的区间的贡献的和。那么
f
[
i
]
[
0
]
f[i][0]
f[i][0] 的值就是
i
i
i 的祖先中离它最近的满足
s
u
≤
s
i
s_u\le s_i
su≤si 的点
u
u
u 的
f
[
u
]
[
s
[
i
]
−
s
[
u
]
]
f[u][s[i]-s[u]]
f[u][s[i]−s[u]],即
f
[
i
]
[
0
]
=
f
[
u
]
[
s
[
i
]
−
s
[
u
]
]
f[i][0]=f[u][s[i]-s[u]]
f[i][0]=f[u][s[i]−s[u]] 。
(想一想为什么
找点的过程类似一个单调栈,不需要真的把树建出来,具体可以看我程序。什么?你说可以倍增找?确实,时间复杂度是可以接受的,但你看看空间限制嘞。
时间复杂度 O ( n ) O(n) O(n) 。
#include <bits/stdc++.h>
#define N 600005
using namespace std;
typedef long long ll;
typedef vector<ll> vec;
const int mod=998244353,M=20;
int n,nn,m,a[N],c[N],l[N],r[N],pos[N],len[N],cnt;
int chk(int x,int y){
if(l[y]<x&&r[x]>y) return 2;
if(l[y]<x||r[x]>y) return 4;
if((r[x]-x)!=(y-l[y])) return 4;
if(r[x]==y&&a[x]) return 1;
return 0;
}
vec ans[N],num[N];
ll to[N],res;
int s[N],fa[N];
int main(){
cin>>n>>m; nn=n+n+1;
for(int i=1;i<=n;i++) cin>>a[i+i];
for(int i=m,x=1;i>=1;i--,x=1ll*x*m%mod) to[i]=x;
for(int i=1;i<=nn;i++){
l[i]=pos[a[i]]; if(l[i]) r[l[i]]=i;
pos[a[i]]=i;
}
int now=1,u=1; len[1]=0;
ans[1].push_back(0),num[1].push_back(0);
for(int i=2;i<=nn;(res+=ans[i][len[i]-s[i]])%=mod,i++){
if(now<i){
s[i]=0; now=i;
ans[i].push_back(0),num[i].push_back(0);
if(!(i&1)) num[i][0]=1; ans[i][0]=to[num[i][0]];
int p=0,q;
while(i+i-now-1>0&&now<nn&&(q=chk(i+i-now-1,now+1))!=4){
now++;
num[i].push_back(num[i][p]+q);
ans[i].push_back((ans[i][p]+(a[now]?to[num[i][p+1]]:0))%mod); p++;
}
len[i]=now-i,u=i; continue;
}
fa[i]=u+u-i; len[i]=min(len[fa[i]],now-i);
while(s[fa[i]]>len[i]) fa[i]=fa[fa[i]];
s[i]=len[i]; ans[i].push_back(ans[fa[i]][len[i]-s[fa[i]]]),num[i].push_back(num[fa[i]][len[i]-s[fa[i]]]);
if(i+len[i]==now){
int p=0,q;
while(i+i-now-1>0&&now<nn&&(q=chk(i+i-now-1,now+1))!=4){
now++;
num[i].push_back(num[i][p]+q);
ans[i].push_back((ans[i][p]+(a[now]?to[num[i][p+1]]:0))%mod); p++;
}
len[i]=now-i,u=i;
}
}
cout<<res;
}