[Manacher+离线+线段树]2015计蒜之道初赛第三场 商品推荐走马灯 题解

题目大意

给出一个长度为 n n n的序列,多次询问一个区间 [ L , R ] [L,R] [L,R]内所有回文子串的权值和。

解题分析

涉及到回文字符串的题目立刻脑回路想到Manacher,那么这题可以考虑从回文中心入手,然后又发现这道题支持离线操作,所以可以离线询问,然后分析每一个回文中心的影响。

那么对于一个询问,会存在回文中心是先碰到左端点还是先碰到右端点,所以可以分成两半分别处理,对于左半区间,肯定会先碰到左端,那么如果回文中心p会扩转到A的话,那么p中心的所有所有的回文子串的权值为

2 ∑ i = A p ( s u m p − s u m i − 1 ) − ∑ i = A p a [ p ] 2\sum_{i=A}^{p} (sum_p-sum_{i-1})-\sum_{i=A}^pa[p] 2i=Ap(sumpsumi1)i=Apa[p]

转化一下,可得到

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

所以前面是只与p有关的权值,后面是具体在不同的位置减去不同的数。等等,右边是前缀和 的前缀和?

所以可以分开用线段树,用两个lazy-tag维护一个值。tgA是一个只与p有关的定值,均摊到区间 [ A , 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值