#莫队,分块#洛谷 3604 美好的每一天

本文探讨了一种针对字符串查询的高效算法,特别是在处理需要判断子区间是否能通过字符移位形成回文串的问题上。文章详细介绍了使用莫队算法进行优化的方法,包括如何维护区间状态、使用异或前缀和技巧以及分块优化策略。此外,还分享了在实现过程中遇到的坑点,如数据类型的正确选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

给定一串字符串,询问多个区间,问有多少个子区间的字母经过移位后可以变成一个回文串


分析

那么回文串当且仅当s[r]s[r]s[r] xor s[l−1]=2ts[l-1]=2^ts[l1]=2t or 0(s表示异或前缀和),也就是只含一个奇数次的字母或不含。
然后这些询问都支持离线,于是想到了莫队,但是莫队需要维护的是l−1∼rl-1\sim rl1r,然后还要用分块优化,但是如何修改呢,可以记录区间内状态的个数,那答案为0就很好办了,那答案为2t(t为任意值)2^t(t为任意值)2t(t)怎么办,可以提前预处理,思路说完了,介绍坑点
unsigned short,啊啊啊啊


代码

#include <cstdio>
#include <cctype>
#include <queue>
#include <algorithm>
#include <cmath>
#define rr register
using namespace std;
typedef unsigned short ust;
struct rec{int l,r,rk,t;}b[60001];
ust w[1<<26]; bool v[1<<26];
int n,m,bk,now,f[60001][26],ans[60001],a[60001],len[60001];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
bool cmp(rec a,rec b){
    return a.t<b.t||(a.t==b.t&&a.r<b.r);
}
inline void add(int x){
    now+=w[a[x]],++w[a[x]];
    for (rr int i=0;i<len[x];++i)
        now+=w[f[x][i]];
}
inline void del(int x){
    --w[a[x]],now-=w[a[x]];
    for (rr int i=0;i<len[x];++i)
        now-=w[f[x][i]];
}
inline void print(int ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
signed main(){
    n=iut(),m=iut(),v[0]=1,bk=n/sqrt(m),getchar();
    for (rr int i=1;i<=n;++i)
        v[a[i]=a[i-1]^(1<<(getchar()-97))]=1;
    for (rr int i=0;i<=n;++i)
    	for (rr int j=0;j<26;++j)
        if (v[a[i]^(1<<j)])
    		f[i][len[i]++]=a[i]^(1<<j);
    for (rr int i=1;i<=m;++i) b[i]=(rec){iut(),iut(),i},b[i].t=b[i].l/bk;
    sort(b+1,b+1+m,cmp); rr int l=b[1].l,r=b[1].l-1; ++w[a[l-1]];
    for (rr int i=1;i<=m;++i){
    	while (l>b[i].l) add(--l-1);
    	while (r<b[i].r) add(++r);
    	while (l<b[i].l) del(l-1),++l;
    	while (r>b[i].r) del(r),--r;
    	ans[b[i].rk]=now;
    }
    for (rr int i=1;i<=m;++i) print(ans[i]),putchar(10);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值