【JZOJ4645】【NOI2016模拟7.16】基因改造计划(Manacher+主席树)

Problem

  给定一个长度为n的字符串S和m个询问,每次询问给出区间[l,r],求区间S[l..r]内回文子串的个数。

Hint

这里写图片描述

Solution

20points:Manacher or 回文自动机

  第0~3个点的n*m较小,我们可以直接把询问区间提取出来, O ( n ) 做一遍Manacher或者回文自动机。
  时间复杂度: O ( n m )

Code

  我并没有打╮(╯▽╰)╭

100points:Manacher+主席树

  先使用Manacher的惯用套路:在原串之间加入分隔符,得到一个长度为2n+1的新串。
  例:aabb→&a&a&b&b&
  这样就可以优雅地解决长度为偶数的回文串了。


  易知以i为中心的回文串个数为 p [ i ] + 1 2
  原询问[l,r]在新串中对应[2l-1,2r+1],令L=2l-1,R=2r+1,囿于回文串的左/右端点不能超过L/R,可得:

A n s = 1 2 [ ( r l + 1 ) k = L R m i n { g [ k ] , k L , R k } ]


  考虑将区间[L,R]折半处理。令mid=(L+R)/2=l+r,则当k≤mid时,min{g[k],k-L,R-k}=min{g[k],k-L}。
  转化一下式子:

k = L m i d m i n { g [ k ] , k L } = k = L m i d m i n { g [ k ] k , L } + k

  用一棵(权值线段树)主席树存储g[k]-k的cnt和sum,每次算完一个g[k]就插进主席树内。查询的时候,我们以-L为中点,左边(g[k]-k≤-L)的区间直接返回sum;右边(g[k]-k>-L)的区间返回cnt*(-L)。当然,这个g[k]-k可能是负数,-L肯定是负数;我们又知道负数区间的权值线段树可能会出问题;所以可以先给它加上个大数。
  对于k>mid的做法类似。


  时间复杂度: O ( ( n + m ) l o g 2 n )

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define A(v) t[v].l
#define B(v) t[v].r
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;

const int N=130050,M=N<<1;
char str[M],s[N];
int i,n,m,len,p[M],r1[M],r2[M],tot,px,l,r,L,R,mid;
ll ans;
struct node
{
    ll s;
    int v,l,r;
}t1[40*M],t2[40*M];

void modify(node*t,int&v,int x,int y)
{
    t[++tot]=t[v]; t[v=tot].v++; t[v].s+=(ll)px;
    if(x==y) return;
    int mid=x+y>>1;
    px<=mid?modify(t,A(v),x,mid):modify(t,B(v),mid+1,y);
}
ll query(node*t,int l,int r,int x,int y)
{
    if(y<=px) return t[r].s-t[l].s;
    if(x> px) return 1ll*(t[r].v-t[l].v)*px;
    int mid=x+y>>1;
    return query(t,A(l),A(r),x,mid) + query(t,B(l),B(r),mid+1,y);
}
//President_Tree

void Manacher()   
{
    int mx=0,idx=0;
    fo(i,1,len-1)
    {
        p[i]=mx>i?min(p[2*idx-i],mx-i):1;
        while(str[i+p[i]]==str[i-p[i]]) p[i]++;
        if(i+p[i]>mx) mx=i+p[i],idx=i;
        px=p[i]-1-i+M; modify(t1,r1[i]=r1[i-1],1,M<<1);
        px=p[i]-1+i;   modify(t2,r2[i]=r2[i-1],1,M<<1);
    }
}

inline ll sum(ll a,ll b){return (a+b)*(b-a+1)/2;}

int main()
{ 
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);
    scanf("%d%d%s",&n,&m,s);
    len=2*n+2;
    str[0]='$';
    fo(i,0,n)
    {
        str[2*i+1]='#';
        str[2*i+2]=s[i];
    }
    Manacher();

    fo(i,1,m)
    {
        scanf("%d%d",&l,&r);
        L=2*l-1; R=2*r+1; mid=l+r;
        ans=sum(L,mid)-sum(mid+1,R);
        px=M-L; ans+=query(t1,r1[L-1],r1[mid],1,M<<1)-1ll*M*(mid-L+1);
        px=R;   ans+=query(t2,r2[mid],r2[R  ],1,M<<1);
        printf("%lld\n",(ll)r-l+1+ans>>1);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80903084