[2019寒假集训day1]三元组(Manacher+差分)

题面

在这里插入图片描述

题解

被题解坑到自闭系列…
不得不说能把一道这么简单的题写的如此好理解也是一种能力

明显首先需要Manacher,然后得到最大半径 p i p_i pi,发现对答案的贡献是一个等差序列。
于是我们可以用6个数组维护等差序列。
分别是 l s 1 i , l s 2 i , l s 3 i , r s 1 i , r s 2 i , r s 3 i ls1_i,ls2_i,ls3_i,rs1_i,rs2_i,rs3_i ls1i,ls2i,ls3i,rs1i,rs2i,rs3i
分别代标从 i i i点开始的首项之和,公差之和,与公差之和的和
以及从 i i i点结束的首项之和,公差之和,与公差之和的和
这里指的公差之和的和就是首先为1得等差序列,统计答案时加上首项之和即可。
按照如下规则差分

int l=(i-p[i])/2,r=(i+p[i]-1)/2,mid=(i-1)/2;
ls1[l]+=(r+1),ls1[mid+1]-=(r+1);
ls2[l+1]--,ls2[mid+1]++;
ls3[mid+1]+=(mid-l);
mid=i/2;
rs1[mid]+=(i+1)/2,rs1[r+1]-=(i+1)/2;
rs2[mid+1]--,rs2[r+1]++;
rs3[r+1]+=(r-mid);

然后答案就是 ( l s 1 [ i ] + l s 3 [ i ] ) ∗ ( r s 1 [ i − 1 ] + r s 3 [ i − 1 ] ) (ls1[i]+ls3[i])*(rs1[i-1]+rs3[i-1]) (ls1[i]+ls3[i])(rs1[i1]+rs3[i1])
细节超多…
复杂度: O ( n ) O(n) O(n)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1000000
#define LL long long
#define MOD 1000000007
int T,len,n;
char LS[MAXN*2+5],s[MAXN*2+5];
int p[MAXN*2+5];
LL ls1[MAXN+5],ls2[MAXN+5],ls3[MAXN+5],rs1[MAXN+5],rs2[MAXN+5],rs3[MAXN+5],ans;
void Init()
{
    s[0]='#';
    for(int i=2;i<=len*2;i+=2)
    {
        s[i-1]=LS[i/2];
        s[i]='#';
    }
    s[len*2]='#';
}
void prefix_s(LL *s1,LL* s2,LL *s3,int p)
{
	s1[p]+=s1[p-1];s1[p]%=MOD;
	s2[p]+=s2[p-1];s2[p]%=MOD;
	s3[p]+=s3[p-1]+s2[p];s3[p]%=MOD;
}
void Manacher()
{
    Init();
    len=strlen(s);
    int pos,mr=0;
    for(int i=1;i<len;i++)
    {
        if(i<mr)p[i]=min(p[pos*2-i],mr-i);
        else p[i]=0;
        while(i+p[i]+1<=len&&i-p[i]-1>=0&&s[i-p[i]-1]==s[i+p[i]+1])p[i]++;
        if(mr<p[i]+i)mr=p[i]+i,pos=i;
        if(p[i]!=0)
        {
        	int l=(i-p[i])/2,r=(i+p[i]-1)/2,mid=(i-1)/2;
        	ls1[l]+=(r+1),ls1[mid+1]-=(r+1);
			ls2[l+1]--,ls2[mid+1]++;
			ls3[mid+1]+=(mid-l);
			mid=i/2;
			rs1[mid]+=(i+1)/2,rs1[r+1]-=(i+1)/2;
			rs2[mid+1]--,rs2[r+1]++;
			rs3[r+1]+=(r-mid);
        }
    }
    return ;
}
int main()
{
	freopen("triple.in","r",stdin);
	freopen("triple.out","w",stdout);
	scanf("%d",&T);
    while(T--)
    {
		scanf("%s",LS+1);
        len=n=strlen(LS+1);
        memset(s,0,sizeof(s));
        for(int i=0;i<=n+1;i++)
        ls1[i]=ls2[i]=ls3[i]=rs1[i]=rs2[i]=rs3[i]=0;
        p[0]=0;ans=0;
		Manacher();
    	for(int i=1;i<=n;i++)
    	{
    		prefix_s(ls1,ls2,ls3,i);
    		prefix_s(rs1,rs2,rs3,i);
    		ans=(ans+((ls1[i]+ls3[i])%MOD)*((rs1[i-1]+rs3[i-1])%MOD)%MOD)%MOD;
    	}
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值