HDU 5371 Hotaru的问题
题意:
Hotaru Ichijou最近沉迷于数学问题。现在她正在玩N序列。
让我们定义N序列,它由三部分组成并满足以下条件:
1.第一部分与第三部分相同
2.第一部分和第二部分是对称的
例如,序列{2,3,4,4,3,2,2,3,4}是N序列,其中第一部分{2,3,4}与第三部分{2,3,4}相同,第一部分{2,3,4}和第二部分{4,3,2}是对称的。
给你n个正整数,你的任务是找到最大的连续子序列,即N序列。
题解:
①首先,肯定要用Manacher求出回文半径
②定义L[i]:以i为回文中心的最长回文串的最左端点
定义R[i]:以i为回文中心的最长回文串的最右端点
③将问题转化一下,我们发现,其实它就是让求回文中心(i,j),满足:
L[i]<=j<i<=R[j]
④可以用并查集维护
⑤初始时 prt[j] = j,
⑥可以发现如果j对i不可行(R[j]<i),那么对i+1也一定不可行,此时令prt[j] = prt[j+1]
⑦对于i,从 j = prt[L[i]]开始跳,注意路径压缩
⑧最后的答案为ans*3,是因为在过程中我们只维护了第二部分也就是i~j的长度
#pragma GCC optimize (2) #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=200000+10; int n,len,s[maxn],L[maxn],R[maxn],P[maxn]; int f[maxn]; int Find(int x){ if(f[x]==x)return x; return f[x]=Find(f[x]); } void Init(); void Manacher(); signed main(){ int t;scanf("%d",&t); for(int i=1;i<=t;i++){ printf("Case #%d: ",i); Init(); } return 0; } void Init(){ scanf("%d",&len); for(int i=1;i<=len;i++)scanf("%d",&s[i]); Manacher(); for(int i=1;i<=n;i++)f[i]=i; int ans=0,tmp=0; for(int i=1;i<=n;i+=2){ int j=Find(L[i]); while(j<i && j>0){ if(i<=R[j]){ tmp=(i-j+1)>>1; ans=max(ans,tmp); break; } else if(i>R[j])f[j]=Find(j+2); j=Find(j+2); } } printf("%d\n",ans*3); //最后的答案为ans*3,是因为在过程中我们只维护了第二部分也就是i~j的长度 } void Manacher(){ memset(P,0,sizeof(P)); n=len<<1|1; for(int i=n;i>=1;i--) if(i&1)s[i]=-1; else s[i]=s[i>>1]; s[0]=-2; int id=0,mx=0; for(int i=1;i<=n;i++){ if(i<mx)P[i]=min(P[id*2-i],mx-i); else P[i]=1; while(s[i+P[i]]==s[i-P[i]])P[i]++; if(i+P[i]>mx)mx=i+P[i],id=i; L[i]=i-P[i]+1; R[i]=i+P[i]-1; } }