牛客练习赛8 F题 莫队算法

题目链接


题意:
给一个长为 n n n 的只含小写字母的字符串
每次查询一个区间 $[l,r] $内,有多少子区间可以重排为一个回文串。

思路:
首先对于一个可以重排为回文串的区间,一定满足至多一个字母的出现次数为奇数,其余字母的出现次数为偶数。

因为只有26个小写字母其只考虑出现次数的奇偶性,故可以考虑压位 2 26 2^{26} 226 表示每一个字母的出现情况。
对每一位求一个前缀和。

对于一个新加入第 x x x个字母 c h ch ch,如果已知当前区间每一个数的出现情况,则
a d d = c n t [ s u m [ x ] ] + ∑ i = 0 25 c n t [ s u m [ x ]   x o r   ( 1 < < i ) ] add = cnt[sum[x]] + \sum_{i=0}^{25} cnt[sum[x] \ xor \ (1<<i)] add=cnt[sum[x]]+i=025cnt[sum[x] xor (1<<i)]

离散化 + 莫队可解。


代码:

#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
 
const int A = 1e5 + 10;
vector<int> v;
int siz,sum[A],n,m;
ll Ans[A],cnt[A],res;
char s[A];
class Que{
public:
    int l,r,id;
    bool operator<(const Que& rhs) const{
        if(l/siz == rhs.l/siz) return r/siz < rhs.r/siz;
        return l/siz < rhs.l/siz;
    }
}Q[A];
class Gra{
public:
    int v,next;
}G[A*50];
int head[A],tot;
 
void update(int pos,int c){
    if(c == -1)  cnt[sum[pos]] += c;
    res += c*cnt[sum[pos]];
    for(int i=head[sum[pos]] ;i!=-1 ;i=G[i].next){
        res += c*cnt[G[i].v];
    }
    if(c == 1)   cnt[sum[pos]] += c;
}
 
void add(int u,int v){
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}
 
void solve(){
    int L = 0,R = -1;res = 0;
    for(int i=1 ;i<=m ;i++){
        int id = Q[i].id;
        while(R < Q[i].r)  update(++R,1);
        while(L > Q[i].l)  update(--L,1);
        while(R > Q[i].r)  update(R--,-1);
        while(L < Q[i].l)  update(L++,-1);
        Ans[id] = res;
    }
 
}
 
int main(){
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
 
    sum[0] = 0;v.push_back(0);
    siz = sqrt(1.0*n);
 
    for(int i=1 ;i<=n ;i++){
        sum[i] = sum[i-1]^(1<<(s[i]-'a'));
        v.push_back(sum[i]);
    }
 
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
 
    int len = v.size();
    memset(head,-1,sizeof(head));tot = 0;
    for(int i=0 ;i<len ;i++){
        for(int j=0 ;j<26 ;j++){
            int x = v[i]^(1<<j);
            int k = lower_bound(v.begin(),v.end(),x) - v.begin();
            if(k>=0 && k<v.size() && v[k] == x) add(i,k);
        }
    }
    for(int i=0 ;i<=n ;i++) sum[i] = lower_bound(v.begin(),v.end(),sum[i]) - v.begin();
 
    for(int i=1 ;i<=m ;i++){
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id = i;Q[i].l--;
    }
    sort(Q+1,Q+1+m);
 
    solve();
    for(int i=1 ;i<=m ;i++){
        printf("%lld\n",Ans[i]);
    }
    return 0;
}

你可能感兴趣的:(牛客练习赛8 F题 莫队算法)