[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,y1] 是合法区间
  • 下面两个条件要满足一个
    • ∣ x − r x ∣ = ∣ y − l y ∣ \left|x-r_x\right|=\left|y-l_y\right| xrx=yly
    • 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](xa,by) 都是合法区间,那么 [ x + y − b , x + y − a ] [x+y-b,x+y-a] [x+yb,x+ya] 也是合法区间。(这个分析不难,但是我懒得在这里画图,有疑惑欢迎来问我。

知道了这个性质,我们就可以用马拉车了。但是你发现这个答案的贡献特别恶心,区间 [ 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](0jlen[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 susi 的点 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;
}
posted @ 2022-10-10 20:18  缙云山车神  阅读(28)  评论(0编辑  收藏  举报