HDU 5785 Interesting manacher + 延迟标记

题意:给你一个串,若里面有两个相邻的没有交集的回文串的话,设为S[i...j] 和 S[j+1...k],对答案的贡献是i*k,就是左端点的值乘上右端点的值。

首先,如果s[x1....j]、s[x2....j]、s[x3....j]....s[xn....j]、是回文串,而且s[j+1...y1]、s[j+1...y2]、s[j+1...y3]、也是回文串,那么这些串对答案的贡献一共就是(x1+x2+...+xn)*(y1+y2+....+yn)

所以想到了用cntL[i]表示:以i这个字符为结尾的回文串的左端点之和,cntR[i]表示:以i这个字符为开始的回文串的右端点之和。

所以答案就是for i=1 to lenstr-1  ans += cntL[i]*cntR[i+1];

通俗点说吧,根据manacher,p[i]就表示以i这个字符为中心的回文串的半径长度,所以有s[i-p[i]...i+p[i]]是一个回文串

所以cntL[i+p[i]]就要加上i-p[i],然后因为s[i-p[i]+1...i+p[i]-1]也是一个回文串,所以cntL[i+p[i]-1]就要加上i-p[i]+1等等之类的。所以就是在[i..i+p[i]]这个区间上,依次加上 i、i-1、i-2、.....i-p[i],这样一个公差为-1的等差数列。这个可以用延迟标记来做

先看简单的,在[L,R]上加上一个数val,我们只需要cnt[L] += val; cnt[R+1] -= val;   然后O(n)扫描,即可得到这个数组的所有值。但是现在是等差数列呢?其实是差不多的,只不过要开多一个数组而已。

首先,如果是在[begin,end]上加上一个以val为首相的等差数列,可以这样考虑。首先cnt[begin] += val;然后可以算出结束的时候的值是多少把?cnt[end+1] -= calc_end_val;即可。然后中间的公差,因为可能有叠加现象,我们要开多个数组subL[i]表示这个位置有多少个-1,这个subL[i]就像一开始的最简单的这样传递下去即可。就是subL[begin] += 1; subL[end+1] -= 1;

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include<stack>
#include <string>
const int maxn = 1000000+20;
const int MOD = 1000000007;
int cntL[maxn],cntR[maxn];
int subL[maxn],subR[maxn];
char str[maxn<<1];
int p[maxn<<1];
void addL (int begin,int end,int val)
{
    if (end<begin) return ;
    cntL[begin] += val;
    cntL[end+1] -= begin-end+val;
    subL[begin+1]++;
    subL[end+1]--;

    cntL[begin] %= MOD;
    cntL[end+1] = (cntL[end+1]+MOD)%MOD;
    subL[begin+1]%=MOD;
    subL[end+1]=(subL[end+1]+MOD)%MOD;
    return ;
}
void addR (int begin,int end,int val)
{
    if (end<begin) return ;
    cntR[begin] += val;
    cntR[end+1] -= begin-end+val;
    subR[begin+1]++;//记录有多少个数列覆盖的,记录公差
    subR[end+1]--;

    cntR[begin] %= MOD;
    cntR[end+1] = (cntR[end+1]+MOD)%MOD;
    subR[begin+1]%=MOD;
    subR[end+1]=(subR[end+1]+MOD)%MOD;
    return ;
}
int manacher (char str[],int lenstr)
{
    str[0]=‘*‘;
    for (int i=lenstr;i>=0;i--)
    {
        str[i+i+2]=str[i+1];
        str[i+i+1]=‘#‘;
    }
    int id=0,maxlen=0;
    for (int i=2;i<=2*lenstr+1;i++)
    {
        if (p[id]+id>i)
        {
            p[i]=min(p[id]+id-i,p[2*id-i]);
        }
        else p[i]=1;
        while (str[i+p[i]] == str[i-p[i]]) ++p[i];
        if (p[id]+id<p[i]+i) id=i;
        maxlen=max(maxlen,p[i]);
    }
    return maxlen-1;
}

void init_arr(int gg)
{
    for (int i=1;i<=gg;++i)
    {
        subL[i] += subL[i-1];
        cntL[i] += cntL[i-1]-subL[i];

        subR[i] += subR[i-1];
        cntR[i] += cntR[i-1]-subR[i];

        subL[i] %= MOD;  subR[i] %= MOD;
        cntL[i]=(cntL[i]+MOD)%MOD; cntR[i]=(cntR[i]+MOD)%MOD;
    }
    return ;
}
void init ()
{
    memset(cntL,0,sizeof cntL); memset(cntR,0,sizeof cntR);
    memset(subL,0,sizeof subL); memset(subR,0,sizeof subR);
    return ;
}
void work ()
{
    init();
    int lenstr = strlen(str+1);
    manacher(str,lenstr);
    for (int i=2;i<=2*lenstr+1;++i)
    {
        int len = p[i]-1;
        if (len==0) continue;
        if (i&1) //‘#‘所在位置
        {
            int pos = i/2;//截断
            //len必须是偶数
            int t = len/2;
            int begin = pos-t+1; //这些细节要慢慢算啦,慢慢推公式
            int end = t+pos;
            addL(pos+1,end,pos);
            addR(begin,pos,end);
        }
        else //字母所在位置
        {
            int pos = i/2;
            len--;
            len/=2;
            int begin = pos-len;
            int end = pos+len;
            addL(pos,end,pos);
            addR(begin,pos,end);
        }
    }
    init_arr(lenstr);
    LL ans = 0;
    for (int i=1;i<=lenstr-1;++i)
    {
        ans += 1LL*cntL[i]*cntR[i+1];
        ans %= MOD;
    }
    printf ("%I64d\n",ans);
    return ;
}
int main ()
{
    #ifdef local
    freopen("data.txt","r",stdin);
    #endif
    while(scanf("%s",str+1)!=EOF) work();
    return 0;
}

时间: 2024-10-24 03:23:44

HDU 5785 Interesting manacher + 延迟标记的相关文章

hdu 5785 Interesting(manacher+前缀和)

题目链接:hdu 5785 Interesting 题意: 有一个长度为n的串(n<=10^6),对 1 <= i <= j < k <= length(s) . 如果[i,j]和[j+1,k]都是回文串.则对答案的贡献为 i*k ,求贡献和. 题解: 详细题解传送门 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 type

HDU5785 Interesting(Manacher + 延迟标记)

题目大概说给一个字符串,找到其所有子串[i...k]满足它是由两个回文串拼成的,求Σi*k. 官方题解这么说的: 用manacher算法O(n)求出所有的回文半径.有了回文半径后,就可以求出cntL[i]表示以i结尾的回文串的起始位置的和cntR[i]表示以i起始的回文串的结尾位置的和,然后就可以求出答案了,这里要注意奇偶长度回文串的不同处理.复杂度O(n). 本渣渣看了好久想了好久..才反应过来x1*y1+x1*y2+x2*y1+x2*y2=(x1+x2)*(y1+y2) ..这个真反应不过来

HDU 5785 interesting

给你一个字符串,问满足i<=j<k并且[i,j]和[j,k]都是回文的时候,i*k的sum值是多少. 看了网上的做法,基本都是每个点算贡献,但是有的记录起来比较考验代码能力. 其中有一种做法是这样的: 首先,推公式,自然不用说.由于每个i*k必然出现在一个以j为中心的双回文串中,所以直接统计左右以当前点和下一个点以左与以右的回文个数,相乘即可. 下面是在O(n)时间内算出左右的回文个数. 定义四个数组. date[1]表示以当前点为右边界的所有回文串的中心的和的两倍. date[2]表示以当前

HDU 1890 Robotic Sort 伸展树的区间反转与延迟标记

延迟标记像极了线段树,不再多说. 区间反转在树伸展到位之后,也变成了简单的递归交换左右儿子. 愈发感觉到伸展树简直太漂亮了,伸展操作更是诱惑到不行 ,总之数据结构太有魅力了. 比较简单,就直接上模板了. #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #in

H - Can you answer these queries? HDU 4027 (线段树+延迟标记+开根号的速度)

H - Can you answer these queries? Time Limit:2000MS Memory Limit:65768KB 64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4027 Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our

HDU 1698 Just a Hook (线段树延迟标记(lazy))

题意:有n个数初始值都为1,m个操作a,b,c,表示把区间[a,b]变为c,求最后n个数的和. 经典区间更新求和问题,需要用到延迟标记(或者说是懒惰标记),简单老说就是每次更新 的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新或询问的时候. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #include<cm

HDU 1698 Just a Hook (区间更新+延迟标记)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 31096    Accepted Submission(s): 15313 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing

E - Just a Hook HDU 1698 (线段树+类似延迟标记)

E - Just a Hook Time Limit:2000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Practice Description In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several

杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)

欢迎"热爱编程"的高考少年--报考杭州电子科技大学计算机学院 Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 20889    Accepted Submission(s): 10445 Problem Description In the game of DotA, Pudge's meat hook