[Manacher+離線+線段樹]2015計蒜之道初賽第三場 商品推薦走馬燈 題解

題目大意

給出一個長度爲nn的序列,多次詢問一個區間[L,R][L,R]內所有迴文子串的權值和。

解題分析

涉及到迴文字符串的題目立刻腦回路想到Manacher,那麼這題可以考慮從迴文中心入手,然後又發現這道題支持離線操作,所以可以離線詢問,然後分析每一個迴文中心的影響。

那麼對於一個詢問,會存在迴文中心是先碰到左端點還是先碰到右端點,所以可以分成兩半分別處理,對於左半區間,肯定會先碰到左端,那麼如果迴文中心p會擴轉到A的話,那麼p中心的所有所有的迴文子串的權值爲

2i=Ap(sumpsumi1)i=Apa[p]2\sum_{i=A}^{p} (sum_p-sum_{i-1})-\sum_{i=A}^pa[p]

轉化一下,可得到

i=Ap(2sumpa[p])2i=Apsumi1\sum_{i=A}^p(2sum_p-a[p])-2\sum_{i=A}^p sum_{i-1}

所以前面是隻與p有關的權值,後面是具體在不同的位置減去不同的數。等等,右邊是前綴和 的前綴和?

所以可以分開用線段樹,用兩個lazy-tag維護一個值。tgA是一個只與p有關的定值,均攤到區間[A,p][A,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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章