bzoj 4866: [Ynoi2017]由乃的商场之旅

版权声明:本文为博主原创文章,爱转载的表明出处就好. https://blog.csdn.net/qq_36797743/article/details/79976357

题意

给你一个序列
每一次给你一个区间[l,r],问你这里面有多少个子区间满足,里面的字母经过排序可以变成回文

前言

以前做的一道题,但是写在题表里面了
bzoj 乱刷计划
感觉这题挺好的,于是特地拿出来复习一下

题解

首先,我们容易发现,如果我们给每一个字母分配一个权值
a就是1b就是2c就是3。。。x就是2i
然后这样就是问有多少个子区间满足它这一段的异或和为0或者若干次幂
我们考虑用莫队来做这题
一个比较显然的暴力莫队是26nn的,详见beginend,做法很简单,但是不是很好写
并且我们发现,这个的话,我们维护辅助数组的话是O(1)的,但是维护答案是O(26)
这个做法不是特别的好优化,于是我们可以把这个方法改造成一个O(26)维护辅助数组,然后维护答案是O(1)
这个的话,我们把辅助数组的定义改为,如果插入这个数的话,会贡献多少答案就可以了
每一次新加入一个状态x,就枚举一个2i,然后让x^(2i)这个位置++
然后就可以做到O(26)维护辅助数组,然后维护答案是O(1)
这样由什么好处呢?
我们可以发现这个复杂度并不平衡
于是我们可以平衡复杂度
具体地说,我们可以让莫队的时候,对于l端点在用一个块的答案,让r单调递增地扫过去
然后每一个询问,再暴力扫一次l到左端点
这样的话,我们可以发现这样的话,移动左端点是O(1)的,移动右端点是O(26)
根据均值不定式修改块的大小就可以做到O(n26n)
但是这样的话还有一种情况没有处理,那就是左端点之间的答案没有计算
这个没有关系,我们这个可以预处理啊
我们预处理一下一个点i到这个块的右端点的答案就可以了
用这个方法就可以吧26放到根号里面去了
同样的,如果你遇到一个维护辅助数组是O(S)的,答案是O(1)的,都可以用这个方法来平衡复杂度
然后一般来说,如果维护辅助数组是O(1)的,但是维护答案是O(S),你都可以用特殊的方法把形式变成上面这种
这里只给出后面一个方法的代码,前面比较慢的写法大同小异,我就不贴了
CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=60005;
inline int read()
{
    int x = 0 , f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
int n,m;
char ss[N];
int p[N];
int size;
int belong[N];
int ha[N];//异或前缀和
unsigned short f[1<<26];
int R[N];
void Ins (int x,int o)
{
    x=ha[x];
    f[x]+=o;
    for (int u=0; u<26; u++) f[x^p[u]]+=o;

}
struct qq
{
    int l,r,id;
} q[N];
bool cmp (qq a,qq b)
{
    return belong[a.l]==belong[b.l]?a.r<b.r:a.l<b.l;
}
LL Ans[N];
int Bl(int l,int r)
{
    int ans2=0;
    for(int i=l; i<=r; ++i)
        Ins(i-1,1),ans2+=f[ha[i]];
    for(int i=l; i<=r; ++i) Ins(i-1,-1);
    return ans2;
}
LL ans,pre[N];
int main()
{
    n=read();
    m=read();
    size=sqrt(26*n);
    //size=1;
    for (int u=1; u<=n; u++) belong[u]=(u-1)/size+1;
    p[0]=1;
    for (int u=1; u<26; u++) p[u]=p[u-1]<<1;
    scanf("%s",ss+1);
    int tot=(n-1)/size+1;
    for (int u=1; u<=n; u++) ha[u]=ha[u-1]^p[ss[u]-'a'];
    for (int u=1; u<=tot; u++)
    {
        R[u]=min(n,u*size);
        ans=0;
        for (int i=R[u]; i>R[u-1]; i--)
        {
            Ins(i,1),ans=ans+f[ha[i-1]],pre[i]=ans;
        }
        for (int i=R[u]; i>R[u-1]; i--)  Ins(i,-1);
    }
    for (int u=1; u<=m; u++) q[u].l=read(),q[u].r=read(),q[u].id=u;
    sort(q+1,q+1+m,cmp);
    int last=0;
    int r=0;//右端点在哪里
    for (int u=1; u<=m; u++)
    {
        if (belong[q[u].l]!=last)//扩过了这一块
        {
            for (int i=R[last]; i<r; i++) Ins(i,-1);
            last=belong[q[u].l];
            r=R[last];
            ans=0;
        }
        if (belong[q[u].l]==belong[q[u].r]) Ans[q[u].id]=Bl(q[u].l,q[u].r);
        else
        {
            while(r<q[u].r) ++r,Ins(r-1,1),ans+=f[ha[r]];
            LL ans2=0;
            Ins(r,1);
            Ins(R[last],-1);
            for(int j=q[u].l; j<=R[last]; ++j) ans2+=f[ha[j-1]];
            Ans[q[u].id]=ans+pre[q[u].l]+ans2;
            Ins(r,-1);
            Ins(R[last],1);
        }
    }
    for (int u=1; u<=m; u++)
        printf("%lld\n",Ans[u]);
    return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页