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

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

Time Limit: 20 Sec Memory Limit: 164 MB
Submit: 192 Solved: 47
[Submit][Status][Discuss]
Description

由乃有一天去参加一个商场举办的游戏。商场派了一些球王排成一行。每个人面前有几堆球。说来也巧,由乃和你
一样,觉得这游戏很无聊,于是决定换一个商场。另一个商场是Deus的,他看到由乃来了,于是想出了一个更有趣
的游戏:写数据结构题这个题是这样的:
我们的情人,不过是随便借个名字,用幻想吹出来的肥皂泡,把信拿去吧,你可以使假戏成真。我本来是无病呻吟
,漫无目的的吐露爱情—现在这些漂泊不定的鸟儿有地方栖息了,你可以从信里看出来。拿去吧—由于不是出自
真心,话就说得格外动听,拿去吧,就这么办吧…由于世界会在7月20日完结,作为救世主,间宫卓司要在19日让
所有人回归天空现在已经是19日傍晚,大家集合在C栋的天台上,一共n个人在他们面前,便是终之空,那终结的天

回归天空是一件庄重的事情,所以卓司决定让大家分批次进行,给每个人给了一个小写字母’a’->’z’作为编号一个
区间的人如果满足他们的编号重排之后可以成为一个回文串,则他们可以一起回归天空,即这个区间可以回归天空
由于卓司是一个喜欢妄想的人,他妄想了m个区间,每次他想知道每个区间中有多少个子区间可以回归天空因为世
界末日要来了,所以卓司的信徒很多
由乃天天做数据结构已经快恶心死了,于是让您帮她做当然,您天天做数据结构题,肯定也觉得恶心死了,不过可
爱即正义!所以这个题还是要做的~
Input

第一行两个数n,m
之后一行一个长为n的字符串,代表每个人的编号
之后m行每行两个数l,r代表每次卓司妄想的区间
n,m<=60000
Output

m行,每行一个数表示答案
Sample Input

6 6

zzqzzq

1 6

2 4

3 4

2 3

4 5

1 1
Sample Output

16

4

2

2

3

1
HINT

Source

By 佚名提供

对于字母 a ,赋予权值 1 ,对于字母 b ,赋予权值 2 ,对于字母 c ,赋予权值 4
那么对于原串的任意一段子区间,取 G 为区间内权值的 xor
这个子区间能重新排列成一个回文串,当且仅当 G=0 G 2 的幂次
用前缀后缀的思路瞎搞搞,能想出一个单次添加或删除头或尾 O(26) 的算法
结合莫队算法能搞出一个 O(26nn) 的算法
然而无论怎么卡常数,这个算法就是过不去。。。。。
没有办法的,理论复杂度太差了!
下面结合平衡规划的思路,给出一个 O(n26n) 的算法
pre[i] 为位置 i 的前缀 xor
那么任意两个 pre 值的 xor 结果就能表示原串中一个子区间的信息
cnt[x] 为当新增一个值为 pre[x] 的端点时新增的合法区间个数
那么新增端点时可以 O(26) 更新好 cnt
B=26n ,对于原串,每 B 个字符分一块
对于每个位置 i ,记 sum[i] 为位置 i 到它所在块的右端点这个区间中的合法区间个数
那么对于所有长度大于 B 的询问,它的答案分三类统计

4866: [Ynoi2017]由乃的商场之旅_第1张图片

图中 p 为左端点 L 所在块的右端点
对于 [L,p] 内部的贡献已经预处理好了
对于 [p+1,R] 的贡献可以通过莫队算法修改的时候统计好
对于横跨两段的贡献,枚举每个 [L1,p1] pre 值,直接用当前的 cnt 即可
于是对于所有长度大于 B 的询问可以用总的 O(n26n) 解决
对于那些长度不超过 B 的询问,从左往右 O(26n) 更新 cnt 数组
在过程中利用类似上面那个办法减掉不存在的贡献即可

#include
#include
#include
#include
#include
#include
using namespace std;

const int M = 50;
const int N = 1 << 26;
const int maxn = 6E4 + 6;
typedef unsigned int u32;
typedef unsigned short u16;

struct data{
    int l,r,Num; data(){}
    data(int l,int r,int Num): l(l),r(r),Num(Num){}
    bool operator < (const data &B) const {return r < B.r;}
};

int n,m,tot,B,pre[maxn],bel[maxn],L[M],R[M],mi[30];
u16 cnt[N]; u32 sum[maxn],Ans[maxn];

vector  v[M],F[maxn],G[maxn];

inline void Add(int x) {for (int i = 0; i <= 26; i++) ++cnt[x ^ mi[i]];}
inline void Del(int x) {for (int i = 0; i <= 26; i++) --cnt[x ^ mi[i]];}

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

inline int Get()
{
    char ch = getchar();
    while (ch < 'a' || 'z' < ch) ch = getchar();
    return 1 << ch - 'a';
}

char s[20];
inline void Print(u32 x)
{
    if (!x) {puts("0"); return;} int len = 0;
    while (x) s[++len] = x % 10,x /= 10;
    for (int i = len; i; i--) putchar(s[i] + '0'); puts("");
}

void Solve1()
{
    u32 now = 0; Add(pre[0]);
    for (int i = 1; i <= n; i++)
    {
        now += cnt[pre[i]];
        for (int j = 0; j < F[i].size(); j++)
            Ans[F[i][j].Num] += now;
        for (int j = 0; j < G[i].size(); j++)
        {
            data d = G[i][j];
            for (int k = d.l; k <= d.r; k++)
                Ans[d.Num] -= cnt[pre[k]];
            Ans[d.Num] -= now;
        }
        Add(pre[i]);
    }
    for (int i = 0; i <= n; i++) Del(pre[i]);
}

void Solve2()
{
    for (int i = 1; i <= tot; i++)
    {
        if (!v[i].size()) continue;
        sort(v[i].begin(),v[i].end());
        int r = R[i]; u32 now = 0; Add(pre[R[i]]);
        for (int j = 0; j < v[i].size(); j++)
        {
            data d = v[i][j];
            while (r < d.r) now += cnt[pre[++r]],Add(pre[r]);
            Ans[d.Num] = now + sum[d.l]; Del(pre[R[i]]);
            for (int j = d.l - 1; j < R[i]; j++)
                Ans[d.Num] += cnt[pre[j]]; Add(pre[R[i]]);
        }
        for (int j = R[i]; j <= r; j++) Del(pre[j]);
    }
}

void Pre_Work()
{
    mi[0] = 1; for (int i = 1; i < 26; i++) mi[i] = mi[i - 1] << 1;
    for (int i = 1; i <= n; i++)
        pre[i] = pre[i - 1] ^ Get(),bel[i] = (i - 1) / B + 1;
    for (int i = 1; i <= bel[n]; i++)
        L[i] = R[i - 1] + 1,R[i] = i * B;
    tot = bel[n]; R[tot] = n;
    for (int i = 1; i <= tot; i++)
    {
        for (int j = R[i] - 1; j >= L[i] - 1; j--)
            Add(pre[j + 1]),sum[j + 1] = sum[j + 2] + cnt[pre[j]];
        for (int j = R[i]; j >= L[i]; j--) Del(pre[j]);
    }
    for (int i = 1; i <= m; i++)
    {
        int l = getint(),r = getint();
        if (bel[l] == bel[r])
        {
            F[r].push_back(data(l,r,i));
            G[l - 1].push_back(data(l,r,i));
        }
        else v[bel[l]].push_back(data(l,r,i));
    }
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    n = getint(); m = getint();
    B = sqrt(26 * n); Pre_Work();
    Solve1(); Solve2();
    for (int i = 1; i <= m; i++) Print(Ans[i]);
    return 0;
}

你可能感兴趣的:(莫队算法,平衡规划)