JZOJ-senior-4645. 【NOI2016模拟7.16】基因改造计划

2 篇文章 0 订阅
1 篇文章 0 订阅
Time Limits: 1000 ms Memory Limits: 524288 KB

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

6 2
ACACAA
1 3
3 6

Sample Output

4
6
这里写图片描述

Data Constraint

这里写图片描述

Solution

题意:

给出一个长度为 n n 的母串 S
每次询问 S[l,r] S [ l , r ] 中回文子串的个数

思路:

先做Manacher,经过观察我们可以得到:
S[k] S [ k ] 为分隔符,则以 S[k] S [ k ] 为中点并且在原串中实际存在的回文串个数为 f[k]2 f [ k ] 2
否则 S[k] S [ k ] 为字母,实际存在的回文串个数为 f[k]+12 f [ k ] + 1 2

对于原询问 [l,r] [ l , r ] ,在新串中对应区间为 [2l1,2r+1] [ 2 ∗ l − 1 , 2 ∗ r + 1 ]
不妨令 L=2l1,R=2r+1 L = 2 ∗ l − 1 , R = 2 ∗ r + 1

考虑枚举回文串的中点,由于回文串的左/右端点不能超过L/R,可以得到

Ans=12[(rl+1)+Rk=Lmin(f[k],kL,Rk)] A n s = 1 2 [ ( r − l + 1 ) + ∑ k = L R m i n ( f [ k ] , k − L , R − k ) ]

可以分别考虑 kL<=Rk k − L <= R − k kL>Rk k − L > R − k 的情况

情形1:当 k<=L+R2=l+r k <= L + R 2 = l + r 时,目标转化为求出 l+rk=Lmin(f[k],kL) ∑ k = L l + r m i n ( f [ k ] , k − L )

情形2:当 k>L+R2=l+r k > L + R 2 = l + r 时,做法类似

这是一个经典的问题,通过分类讨论去掉 min m i n 以后,问题转化为求二维平面矩形内点权和,据说可以使用可持久化线段树来维护这个二维点集,可以做到单次询问 O(logn) O ( l o g n ) ,当然,由于我很弱,所以我用的是扫描线

对于情形1:我们考虑从后往前做,由于 L L 在减小,对于一个 k>L 来说,左边界的减小会使它对答案的贡献增大,而当 L<kf[k] L < k − f [ k ] 的时候,无论 L L 再如何左移, 由于原来以 k 为中心的最长字符串长度的限制, k k 位置对答案的贡献都将不再增加,所以我们每次对 L 后可以增加贡献的增加贡献,再将不可增加的打个标记,最后再求出答案即可

对于情形2:与情形1类似

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

Code

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<vector>

#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define ll long long
#define N 130010

using namespace std;

int n,m,i,opl,opr,opx;
int f[2*N],d[8*N],p[8*N],c[N][2];
ll sum,ans[N],s[8*N];
char a[2*N],b[2*N];

vector<int> L[2*N],R[2*N],S[2*N];

void read(int &n)
{
    int x=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    n=w?-x:x; 
}

void write(ll x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

int Min(int x,int y)
{
    return x<y?x:y;
}

void Manacher()
{
    int i,x=0,p=0;
    fo(i,1,n) b[2*i-1]='#',b[2*i]=a[i];
    n=2*n+1,b[n]='#',b[n+1]='@'; 
    fo(i,1,n)
    {
        f[i]=x>i?Min(x-i,f[2*p-i]):1;
        while(b[i-f[i]]==b[i+f[i]]) f[i]++;
        if(f[i]+i>x) x=f[i]+i,p=i;
    }
    fo(i,1,n) f[i]--;
}

void down(int x,int i,int m,int j)
{
    ll u=x+x,v=x+x+1;
    s[u]+=(m-i+1-d[u])*p[x];
    s[v]+=(j-m-d[v])*p[x];
    p[u]+=p[x],p[v]+=p[x],p[x]=0;
}

void update(int x)
{
    d[x]=d[x+x]+d[x+x+1];
    s[x]=s[x+x]+s[x+x+1];
}

void calc(int x,int l,int r)
{
    if(opl>opr||opl<1||opr>n) return;
    if(opl<=l&&opr>=r)
    {
        if(opx==1) s[x]+=r-l+1-d[x],p[x]++;
        else if(opx==2) sum+=s[x];
        else s[x]=f[opl],d[x]=1,p[x]=0;
        return;
    }
    int mid=(l+r)>>1; down(x,l,mid,r);
    if(opl<=mid) calc(x+x,l,mid);
    if(opr>mid) calc(x+x+1,mid+1,r);
    update(x);
}

int main()
{
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);
    scanf("%d%d",&n,&m);
    scanf("%s",a+1);
    fo(i,1,m)
    {
        read(c[i][0]),read(c[i][1]);
        L[2*c[i][0]-1].push_back(i);
        R[2*c[i][1]+1].push_back(i);
    }
    Manacher();
    fd(i,n,1)
    {
        S[i-f[i]].push_back(i);
        for(;!S[i].empty();S[i].pop_back())
        {
            opl=opr=S[i].back(),opx=3;
            calc(1,1,n);
        }
        opl=i+1,opr=n,opx=1,calc(1,1,n);
        for(;!L[i].empty();L[i].pop_back())
        {
            int k=L[i].back();
            opl=i,opr=c[k][0]+c[k][1],opx=2;
            sum=0,calc(1,1,n),ans[k]+=sum;
        }
    }
    memset(s,0,sizeof(s));
    memset(d,0,sizeof(d));
    memset(p,0,sizeof(p));
    fo(i,1,n)
    {
        S[i+f[i]].push_back(i);
        for(;!S[i].empty();S[i].pop_back())
        {
            opl=opr=S[i].back(),opx=3;
            calc(1,1,n);
        }
        opl=1,opr=i-1,opx=1,calc(1,1,n);
        for(;!R[i].empty();R[i].pop_back())
        {
            int k=R[i].back();
            opl=c[k][0]+c[k][1]+1,opr=i,opx=2;
            sum=0,calc(1,1,n),ans[k]+=sum;
        }
    }
    fo(i,1,m) write((ans[i]+c[i][1]-c[i][0]+1)/2),putchar('\n');
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信息学奥赛是一项重要的竞赛活动,旨在培养学生的计算机科学和信息技术能力。省选是指在全省范围内举办的预赛,参赛选手需要通过省选才能晋级到更高层次的比赛。在省选中发挥出色的选手将有机会代表本省参加全国信息学奥林匹克竞赛(NOI)。 而noi_pdf-2020.12.29.rar 是一个压缩文件的命名,其中包含了一份关于NOI的PDF文档。这个文件可能包含了有关NOI的相关资料,例如竞赛规则、题目类型、考试要求等等。通过研究这个文件,选手可以更好地准备信息学奥赛,提高竞赛成绩。 对于想要参加信息学奥赛的同学们来说,可以利用这份PDF文档来深入了解NOI的要求和考试内容。首先,可以仔细阅读竞赛规则,了解比赛的时间、地点、参赛资格等重要信息。其次,可以通过研究题目类型和考试要求,明确自己需要学习和复习的内容,制定合理的备考计划。此外,可以通过查阅往年的竞赛题目和解答,进行练习和模拟考试,提高解题能力和应变能力。 综上所述,信息学奥赛省选和noi_pdf-2020.12.29.rar对于想要参加信息学奥赛的同学们来说都有重要的意义。省选是选拔出优秀选手的一个重要阶段,而noi_pdf-2020.12.29.rar则提供了有关NOI的重要资料,帮助选手更好地准备竞赛。希望通过努力学习和准备,同学们可以在比赛中取得优异成绩,提升自己的计算机科学和信息技术能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值