【JZOJ4645】基因改造计划

37 篇文章 0 订阅
2 篇文章 0 订阅

Description

给出一个字符串和多组询问,每次询问一个区间内不同位置的回文串个数。

Solution

这里不是询问本质不同的回文串个数,不能用莫队+回文树。

考虑偶回文串较难处理,先在字符中间插入分隔符,变成长度为 2n+1 2 n + 1 的串,用manacher处理得到以 i i 为中心长度大于1的回文串个数pi,考虑原来的询问 [l,r] [ l , r ] ,变成了新串上的 [2l1,2r+1] [ 2 l − 1 , 2 r + 1 ] ,设 L=2l1 L = 2 l − 1 R=2r+1 R = 2 r + 1 ,观察一下发现区间答案为:

12(rl+1+i=LRmin(pi,iL,Ri)) 1 2 ( r − l + 1 + ∑ i = L R m i n ( p i , i − L , R − i ) )

mid=L+R2 m i d = L + R 2
i>mid i > m i d 时( i<=mid i <= m i d 方法类似),需要求 Ri=mid+1min(pi,Ri) ∑ i = m i d + 1 R m i n ( p i , R − i ) 的值。
将所有询问离线,考虑一个位置 i i 的值在Ri+pi pi p i ,其余情况则时 Ri R − i (记这种情况为特殊位置)。 R R 端点右移,遇到询问时统计一下特殊位置的个数与特殊位置上的i的和就可以计算出局部答案。

至于 i<=mid i <= m i d ,把字符串倒过来再做一遍即可。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int N=3e5+10;
int f[N];
char s[N],a[N];
int tot=0;
void manacher(){
    int lm=0;
    int id,l=tot;
    for(int i=1;i<=l;i++){
        f[i]=lm>i?min(f[2*id-i],lm-i):1;
        while(a[i+f[i]]==a[i-f[i]]) f[i]++;
        if(f[i]+i>lm) lm=f[i]+i,id=i;
    }
    fo(i,1,l) f[i]--;
}
struct node{
    int l,r,mid,p;
}b[N];
int n,m;
ll an[N],cd[N];
bool cmp(node x,node y){
    return x.r<y.r;
}
struct tree{
    ll t,s,sf;
}tr[N<<1];
void update(int v){
    tr[v].t=tr[v<<1].t+tr[(v<<1)+1].t;
    tr[v].s=tr[v<<1].s+tr[(v<<1)+1].s;
    tr[v].sf=tr[v<<1].sf+tr[(v<<1)+1].sf;
}
void build(int v,int l,int r){
    if(l==r){
        tr[v].s=l,tr[v].t=1,tr[v].sf=0;
        return;
    }
    int mid=(l+r)>>1;
    build(v<<1,l,mid),build((v<<1)+1,mid+1,r);
    update(v);
}
void modify(int v,int l,int r,int x){
    if(l==r){
        tr[v].s=tr[v].t=0,tr[v].sf=f[x];
        return;
    }
    int mid=(l+r)>>1;
    x<=mid?modify(v<<1,l,mid,x):modify((v<<1)+1,mid+1,r,x);
    update(v);
}
int now;
ll sum(int v,int l,int r,int x,int y){
    if(l==x && r==y) return tr[v].sf+(ll)now*tr[v].t-tr[v].s;
    int mid=(l+r)>>1;
    if(y<=mid) return sum(v<<1,l,mid,x,y);
    else if(x>mid) return sum((v<<1)+1,mid+1,r,x,y);
    else return sum(v<<1,l,mid,x,mid)+sum((v<<1)+1,mid+1,r,mid+1,y);
}
vector<int> dl[N];
void solve(){
    build(1,1,tot);
    fo(i,1,tot) dl[i+f[i]].push_back(i);
    int p=0;
    fo(i,1,tot){
        now=i;
        int o=dl[i].size();
        fo(j,0,o-1) modify(1,1,tot,dl[i][j]);
        while(p<m && b[p+1].r==i){
            p++;
            an[b[p].p]+=sum(1,1,tot,b[p].mid,b[p].r);
            cd[b[p].p]=(b[p].r-b[p].l)/2;
        }
    }
    fo(i,1,tot) dl[i].clear();
}
int main()
{
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);
    scanf("%d %d",&n,&m);
    scanf("%s",s+1);
    fo(i,1,n) a[++tot]='#',a[++tot]=s[i];
    a[++tot]='#';
    a[0]='*';
    manacher();
    fo(i,1,m){
        scanf("%d %d",&b[i].l,&b[i].r),b[i].p=i;
        b[i].l=2*b[i].l-1,b[i].r=2*b[i].r+1;
        b[i].mid=(b[i].l+b[i].r)/2;
    }
    sort(b+1,b+m+1,cmp);
    solve();
    reverse(f+1,f+tot+1);
    fo(i,1,m){
        int l=b[i].l,r=b[i].r;
        b[i].l=tot-r+1,b[i].r=tot-l+1;
        b[i].mid=(b[i].l+b[i].r)/2+1;
    }
    sort(b+1,b+m+1,cmp);
    solve();
    fo(i,1,m) printf("%lld\n",(an[i]+cd[i])/2);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值