題意簡述
給你一個長度爲 的數組 ,問你有多少個區間,滿足:
- 長度爲偶數
- 前一半和後一半循環同構
思路
兩個串 循環同構 ,那麼一定可以把 分成兩個串 接起來,然後把 表示成 的形式。
就比如 ,那麼 。
然後現在這兩個串相鄰了,也就是有一段連續的 的形式。考慮枚舉中間這個劃分線,然後計算兩邊有多少滿足條件的。這也許能用哈希水過,但是我們要想一個正經辦法(*▽*)
一般我們遇到這樣的“分塊迴文” 的問題,都是怎麼做的呢⊙(・◇・)?
聯想一下 codeforces 932G 這個題,我們可以把前面一半的 反過來,然後一個隔一個的插入到後半邊的 裏面去。這樣插入完就會變成兩個長度爲偶數的迴文串拼在一起啦 (/≧▽≦/)
關於它爲啥會變成這樣
先考慮一個串的情況。現在有一個 ,假設它由三個字符 構成。我們把它反過來,然後一個隔一個的插入,變成:
新插入的用紅色表示,原來就有的用黑色表示。然後我們發現它變成了一個迴文串!
容易歸納證明,無論它長度多少,這樣子做總會變成一個迴文串。
然後考慮兩個串 拼一塊的情況。我們把 反過來,一個隔一個的插入到 中,會變成什麼呢?
設 是 反過來的串。然後顯然 。我們把它插入到 中之後,前面 個串會先變成一個長度爲 的迴文串,後面還有 個串,會變成長度爲 的迴文串。於是總體就變成了一個長度爲 的兩個偶數長度迴文串。
舉個例子,,這樣插入完之後變成 。 它等於 。
(你們可能不知道這公式有多難打,就爲了舉個形象的例子… 唉o(╥﹏╥)o)
然後我們只需要用 Manacher 統計一下有多少個這樣的迴文串即可(~ ̄▽ ̄~)
設 表示從 往前最多能跳多少長度,使得跳過的部分是迴文串。然後 是迴文中心。
用 記錄上一個迴文前綴的位置。當前位置在 ,如果 是個迴文串,或者 是個迴文串,那麼當前的 就是合法的~☆,答案 ++。
那麼如何判斷 是不是迴文串呢⊙(・◇・)?設 ,看看是否滿足 即可。
代碼
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 14444
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define iter(a,p) (a.begin()+p)
int I()
{
int x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return (x=(f==1)?x:-x);
}
void Rd(int cnt,...)
{
va_list args; va_start(args,cnt);
F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
va_end(args);
}
int n;
int a[N];
void Input()
{
n=I();
F(i,1,n) a[i]=I();
}
int s[N];
int f[N],pre[N];
int calc(int p)
{
int l=p,r=p+1;
int cnt=0;
while(l>=1 and r<=n) s[++cnt]=a[l],s[++cnt]=a[r],--l,++r;
F(i,0,cnt+1) f[i]=pre[i]=0;
int id=1,Max=1;
F(i,1,cnt)
{
f[i]=min(max(Max-i,0),f[id+(id-i)]);
while(i+f[i]+1<=n and i-f[i]>0 and s[i+f[i]+1]==s[i-f[i]]) ++f[i];
if (i+f[i]>=Max) Max=i+f[i],id=i;
pre[i+f[i]]=max(pre[i+f[i]],f[i]);
}
D(i,cnt,1) pre[i]=max(pre[i],pre[i+1]-1);
Ds(i,cnt,1,i-=2) pre[i]*=2;
int ans=0,last=0;
Fs(i,2,cnt,i+=2)
{
if (f[i/2]==i/2) last=i;
if (f[(i+last)/2]>=(i-last)/2 or f[(i-pre[i])/2]>=(i-pre[i])/2) ++ans;
}
return ans;
}
void Soviet()
{
int ans=0;
F(i,1,n-1) ans+=calc(i);
printf("%d\n",ans);
}
#define Flan void
Flan IsMyWife()
{
Input();
Soviet();
}
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}