BZOJ4866: [Ynoi2017]由乃的商场之旅 莫队

http://www.lydsy.com/JudgeOnline/problem.php?id=4866
询问一个字符串区间内有多少子区间重排后能形成回文串。
由于字符集只有26,可以给每个字母分配一个2的幂次作为权值,则相当于询问区间异或和是否为2的幂次或0
直接很难维护,那么考虑莫队,维护一个桶记录当前区间内所有前缀的异或和,若在前端插入删除则打上全局标记,然后每次插入删除时枚举每个2的幂次更新答案即可。时间复杂度n*sqrt(n)*26
还是那句话,BZOJ机子太慢,本地跑得游刃有余,交上去就过不了。。。
那么考虑卡卡常,若字符集小则复杂度低,若字符集大则答案小,那么预处理出以每个点开头或结尾,第一个合法的另一端在哪里,更新答案时若不可能有贡献就直接跳过,这样就可通过了。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define gm 60005
using namespace std;
typedef long long ll;
struct Istream
{
    static const size_t str=1<<16;
    char buf[str],*s,*t;
    Istream():buf(),s(),t(){}
    char get()
    {
        return (s==t)?(t=buf+fread(s=buf,1,str,stdin),*s++):(*s++);
    }
    Istream& operator>> (int &x)
    {
        x=0; register char c;
        do c=get(); while(c<'0'||c>'9');
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=get();
        return *this;
    }
    Istream& operator>> (char *s)
    {
        register char c;
        do c=get(); while(c<'a'||c>'z');
        while(c>='a'&&c<='z') *s++=c,c=get();
        return *this;
    }
}cin;
struct Ostream
{
    static const size_t str=1<<16;
    char buf[str],*s,*t;
    Ostream():buf(),s(buf),t(buf+str){}
    ~Ostream(){fwrite(buf,1,s-buf,stdout);}
    void put(char c)
    {
        (s==t)?(fwrite(s=buf,1,str,stdout),*s++=c):(*s++=c);
    }
    Ostream& operator<< (ll x)
    {
        if(!x) return operator<<('0');
        int a[22],t=1;
        while(x) a[t++]=x%10,x/=10;
        while(--t) put(a[t]+'0');
        return *this;
    }
    Ostream& operator<< (char c){return put(c),*this;}
}cout;
const char endl='\n';
int n,m,size;
int pre[gm],nex[gm];
char s[gm];
int t[gm];
ll ans[gm];
struct query
{
    int l,r;
    ll* ptr;
    bool operator< (const query& ano) const
    {
        return l/size!=ano.l/size?l/size<ano.l/size:r<ano.r;
    }
}q[gm];
int l=1,r=0;
ll now=0;
unsigned short cnt[1<<26];
int mark=0;
int tot=0;
int a=0;
int val[128];
inline void append_front(int x)
{
    tot^=x; mark^=x; ++cnt[x^mark];
    if(nex[l]>r) ++now;
    else
    {
        now+=cnt[mark];
        for(int i=0;i<a;++i) now+=cnt[(1<<i)^mark];
    }
}
inline void contract_front(int x)
{
    if(nex[l-1]>r) --now;
    else
    {
        for(int i=0;i<a;++i) now-=cnt[(1<<i)^mark];
        now-=cnt[mark];
    }
    --cnt[x^mark]; mark^=x; tot^=x;
}
inline void append_back(int x)
{
    tot^=x; ++cnt[mark];
    if(pre[r]<l) ++now;
    else
    {
        now+=cnt[tot^mark];
        for(int i=0;i<a;++i) now+=cnt[(1<<i)^mark^tot];
    }
    --cnt[mark]; ++cnt[tot^mark];
}
inline void contract_back(int x)
{
    --cnt[tot^mark]; ++cnt[mark];
    if(pre[r+1]<l) --now;
    else
    {
        for(int i=0;i<a;++i) now-=cnt[(1<<i)^mark^tot];
        now-=cnt[tot^mark];
    }
    --cnt[mark]; tot^=x;
}
int main()
{
    cin>>n>>m>>(s+1);
    for(int i=1;i<=n;++i)
    {
        int c=s[i];
        if(!val[c]) val[c]=1<<(a++);
        t[i]=val[c];
    }
    int last=0; memset(cnt,0,sizeof cnt);
    for(int i=1;i<=n;++i)
    {
        int kre=last^t[i];
        pre[i]=cnt[kre];
        for(int j=0;j<a;++j)
        {
            int pos=cnt[kre^(1<<j)];
            if(pos>pre[i]) pre[i]=pos;
        }
        cnt[last]=i; last=kre;
    }
    last=0; memset(cnt,0,sizeof cnt);
    for(int i=n;i>=1;--i)
    {
        int kre=last^t[i];
        nex[i]=(cnt[kre])?(cnt[kre]):n+1;
        for(int j=0;j<a;++j)
        {
            int pos=cnt[kre^(1<<j)];
            if(!pos) pos=n+1;
            if(pos<nex[i]) nex[i]=pos;
        }
        cnt[last]=i; last=kre;
    }
    size=sqrt(n*a);
    for(int i=1;i<=m;++i)
    {
        cin>>q[i].l>>q[i].r;
        q[i].ptr=ans+i;
    }
    sort(q+1,q+m+1);
    memset(cnt,0,sizeof cnt);
    for(int i=1;i<=m;++i)
    {
        int x=q[i].l,y=q[i].r;
        while(x<l) append_front(t[--l]);
        while(y>r) append_back(t[++r]);
        while(x>l) contract_front(t[l++]);
        while(y<r) contract_back(t[r--]);
        *q[i].ptr=now;
    }
    for(int i=1;i<=m;++i) cout<<ans[i]<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值