題目大意
給出一個長度爲的序列,多次詢問一個區間內所有迴文子串的權值和。
解題分析
涉及到迴文字符串的題目立刻腦回路想到Manacher,那麼這題可以考慮從迴文中心入手,然後又發現這道題支持離線操作,所以可以離線詢問,然後分析每一個迴文中心的影響。
那麼對於一個詢問,會存在迴文中心是先碰到左端點還是先碰到右端點,所以可以分成兩半分別處理,對於左半區間,肯定會先碰到左端,那麼如果迴文中心p會擴轉到A的話,那麼p中心的所有所有的迴文子串的權值爲
轉化一下,可得到
所以前面是隻與p有關的權值,後面是具體在不同的位置減去不同的數。等等,右邊是前綴和 的前綴和?
所以可以分開用線段樹,用兩個lazy-tag維護一個值。tgA是一個只與p有關的定值,均攤到區間,而tgB是與區間長度相關的值,具體見代碼。
把序列倒置一下(順便直接把p也倒置而不用再求一次Manacher),然後就可以處理右半區間。最後關注一下細節(答案要/2,貌似與Manacher的輔助字符有關)。
示例代碼
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=200005,maxq=100005;
int n,m,N,sum[maxn],a[maxn],p[maxn]; LL ans[maxq],S[maxn];
struct data{int L,R,id;}now[maxq],q[maxq];
struct seg{
int tL[maxn<<2],tR[maxn<<2],tgB[maxn<<2];
LL tgA[maxn<<2],val[maxn<<2];
inline void AddtagA(int x,LL tem){val[x]+=tem*(tR[x]-tL[x]+1); tgA[x]+=tem;}
inline void AddtagB(int x,int tem){val[x]+=(S[tR[x]]-S[tL[x]-1])*tem; tgB[x]+=tem;}
inline void Pushup(int x){val[x]=val[x<<1]+val[x<<1|1];}
inline void Pushdown(int x){
if (tgA[x]){AddtagA(x<<1,tgA[x]); AddtagA(x<<1|1,tgA[x]); tgA[x]=0;}
if (tgB[x]){AddtagB(x<<1,tgB[x]); AddtagB(x<<1|1,tgB[x]); tgB[x]=0;}
}
inline void Build(int x,int L,int R){
tL[x]=L; tR[x]=R; tgA[x]=tgB[x]=val[x]=0; if (L==R) return;
int mid=L+(R-L>>1); Build(x<<1,L,mid); Build(x<<1|1,mid+1,R); Pushup(x);
}
inline void AddA(int x,int L,int R,int tem){
if (L>tR[x]||R<tL[x]) return; if (L<=tL[x]&&tR[x]<=R){AddtagA(x,tem); return;}
int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
if (R<=mid) AddA(x<<1,L,R,tem); else if (L>mid) AddA(x<<1|1,L,R,tem);
else {AddA(x<<1,L,mid,tem); AddA(x<<1|1,mid+1,R,tem);} Pushup(x);
}
inline void AddB(int x,int L,int R,int tem){
if (L>tR[x]||R<tL[x]) return; if (L<=tL[x]&&tR[x]<=R){AddtagB(x,tem); return;}
int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
if (R<=mid) AddB(x<<1,L,R,tem); else if (L>mid) AddB(x<<1|1,L,R,tem);
else {AddB(x<<1,L,mid,tem); AddB(x<<1|1,mid+1,R,tem);} Pushup(x);
}
inline LL Ask(int x,int L,int R){
if (L>tR[x]||R<tL[x]) return 0; if (L<=tL[x]&&tR[x]<=R) return val[x];
int mid=tL[x]+(tR[x]-tL[x]>>1); Pushdown(x);
if (R<=mid) return Ask(x<<1,L,R); else if (L>mid) return Ask(x<<1|1,L,R);
else return Ask(x<<1,L,mid)+Ask(x<<1|1,mid+1,R);
}
}T;
inline bool cmp(const data &a,const data &b){return a.R<b.R;}
inline char nc(){
static char buf[100000],*pa=buf,*pb=buf;
return pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++;
}
inline void readi(int &x){
x=0; char ch=nc(),lst='+';
while ('0'>ch||ch>'9'){lst=ch; ch=nc();}
while ('0'<=ch&&ch<='9'){x=x*10+ch-'0'; ch=nc();}
if (lst=='-') x=-x;
}
void Manacher(){
int id=0,mx=0;
for (int i=1;i<=n;i++){
if (i<mx) p[i]=min(mx-i,p[2*id-i]); else p[i]=1;
while (a[i+p[i]]==a[i-p[i]]&&i+p[i]<=n&&i-p[i]>=1) p[i]++;
if (i+p[i]>mx){id=i; mx=i+p[i];}
}
}
int main()
{
freopen("paild.in","r",stdin);
freopen("paild.out","w",stdout);
readi(N); readi(m); a[n=1]=1e9; for (int i=1;i<=N;i++,a[++n]=1e9) readi(a[++n]); Manacher();
for (int i=1;i<=m;i++){readi(q[i].L); readi(q[i].R); q[i].L=(q[i].L<<1)-1; q[i].R=(q[i].R<<1)+1;}
sum[0]=S[0]=0; for (int i=1;i<=n;i++){sum[i]=sum[i-1]+((a[i]<1e9)?a[i]:0); S[i]=S[i-1]+sum[i-1];}
for (int i=1;i<=m;i++){now[i].L=q[i].L; now[i].R=q[i].L+(q[i].R-q[i].L>>1); now[i].id=i;}
T.Build(1,1,n); sort(now+1,now+m+1,cmp);
for (int i=1,j=1;i<=n&&j<=m;i++){
T.AddA(1,i-p[i]+1,i,(sum[i]<<1)-(a[i]<1e9?a[i]:0)); T.AddB(1,i-p[i]+1,i,-2);
for (;j<=m&&now[j].R==i;j++) ans[now[j].id]+=T.Ask(1,now[j].L,now[j].R);
}
reverse(a+1,a+n+1); reverse(p+1,p+n+1);
sum[0]=S[0]=0; for (int i=1;i<=n;i++){sum[i]=sum[i-1]+((a[i]<1e9)?a[i]:0); S[i]=S[i-1]+sum[i-1];}
for (int i=1;i<=m;i++){now[i].L=n-q[i].R+1; now[i].R=n-(q[i].L+(q[i].R-q[i].L>>1)); now[i].id=i;}
T.Build(1,1,n); sort(now+1,now+m+1,cmp);
for (int i=1,j=1;i<=n&&j<=m;i++){
T.AddA(1,i-p[i]+1,i,(sum[i]<<1)-(a[i]<1e9?a[i]:0)); T.AddB(1,i-p[i]+1,i,-2);
for (;j<=m&&now[j].R==i;j++) ans[now[j].id]+=T.Ask(1,now[j].L,now[j].R);
}
for (int i=1;i<=m;i++) printf("%lld\n",ans[i]>>1);
return 0;
}