[Luogu P4135] [BZOJ 2821] 作诗

洛谷传送门
BZOJ传送门

题目描述

神犇SJY虐完HEOI之后给傻×LYD出了一题:

SHY是T国的公主,平时的一大爱好是作诗。

由于时间紧迫,SHY作完诗之后还要虐OI,于是SHY找来一篇长度为 N N 的文章,阅读M次,每次只阅读其中连续的一段 [l,r] [ l , r ] ,从这一段中选出一些汉字构成诗。因为SHY喜欢对偶,所以SHY规定最后选出的每个汉字都必须在 [l,r] [ l , r ] 里出现了正偶数次。而且SHY认为选出的汉字的种类数(两个一样的汉字称为同一种)越多越好(为了拿到更多的素材!)。于是SHY请LYD安排选法。

LYD这种傻×当然不会了,于是向你请教……

问题简述: N N 个数,M组询问,每次问 [l,r] [ l , r ] 中有多少个数出现正偶数次。

输入输出格式

输入格式:

输入第一行三个整数 n n c以及 m m 。表示文章字数、汉字的种类数、要选择M次。

第二行有 n n 个整数,每个数Ai [1,c] [ 1 , c ] 间,代表一个编码为 Ai A i 的汉字。

接下来 m m 行每行两个整数l r r ,设上一个询问的答案为ans(第一个询问时 ans=0 a n s = 0 ),令 L=(l+ans)modn+1 L = ( l + a n s ) mod n + 1 , R=(r+ans)mod n+1 R = ( r + a n s ) mod   n + 1 ,若 L>R L > R ,交换 L L R,则本次询问为 [L,R] [ L , R ]

输出格式:

输出共 m m 行,每行一个整数,第i个数表示SHY第 i i 次能选出的汉字的最多种类数。

输入输出样例

输入样例#1:

5 3 5
1 2 2 3 1
0 4
1 2
2 2
2 3
3 5

输出样例#1:

2
0
0
0
1

说明

对于100%的数据,1n,c,m105

解题分析

这道题用线段树的话区间信息不好合并, 于是大力分块…

我们可以先 O(NN) O ( N N ) 预处理块内的每个颜色出现的次数, O(NN) O ( N N ) 预处理块 A A 到块B的答案。询问时边角暴力, 加上中间一块预处理的答案即可。

详见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdlib>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 100050
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
const int UP = std::sqrt(MX) + 1;
int dot, siz, q, ctyp, bd, lastans;
int bel[MX], sum[333][MX], ans[333][333], cnt[MX], dat[MX];
IN int query(R int lef, R int rig)
{
    lastans = 0;
    if(lef > rig) std::swap(lef, rig);
    int l = bel[lef], r = bel[rig];
    if(r - l <= 1)//相邻块或同块直接暴力
    {
        for (R int i = lef; i <= rig; ++i)
        {
            ++cnt[dat[i]];
            if(!(cnt[dat[i]] & 1)) ++lastans;
            else if(cnt[dat[i]] > 2) --lastans;
        }
        for (R int i = lef; i <= rig; ++i) --cnt[dat[i]];
        return lastans;
    }
    lastans = ans[l + 1][r - 1];
    int lb = l * siz, buf;
    for (R int i = lef; i <= lb; ++i)//两边分别暴力
    {
        ++cnt[dat[i]];
        buf = cnt[dat[i]] + sum[r - 1][dat[i]] - sum[l][dat[i]];
        if(!(buf & 1)) ++lastans;
        else if(buf > 2) --lastans;
    }
    for (R int i = (r - 1) * siz + 1; i <= rig; ++i)
    {
        ++cnt[dat[i]];
        buf = cnt[dat[i]] + sum[r - 1][dat[i]] - sum[l][dat[i]];
        if(!(buf & 1)) ++lastans;
        else if(buf > 2) --lastans;
    }
    for (R int i = lef; i <= lb; ++i) --cnt[dat[i]];
    for (R int i = (r - 1) * siz + 1; i <= rig; ++i) --cnt[dat[i]];
    return lastans;
}
int main(void)
{
    int cot, a, b;
    in(dot), in(ctyp), in(q); siz = std::sqrt(dot) + 1;
    for (R int i = 1; i <= dot; ++i)
    {
        in(dat[i]); bel[i] = (i - 1) / siz + 1;
        ++sum[bel[i]][dat[i]];
    } bd = bel[dot];
    for (R int i = 1; i <= ctyp; ++i)
    for (R int j = 1; j <= bd; ++j) sum[j][i] += sum[j - 1][i];//预处理每块字符数量的前缀和
    for (R int i = 1; i <= bd; ++i)
    {
        cot = 0;//预处理块间的答案
        for (R int j = (i - 1) * siz + 1; j <= dot; ++j)
        {
            ++cnt[dat[j]];
            if(!(cnt[dat[j]] & 1)) ++cot;
            else if(cnt[dat[j]] > 2) --cot;
            ans[i][bel[j]] = cot;
        }
        for (R int j = (i - 1) * siz + 1; j <= dot; ++j) --cnt[dat[j]];
    }
    W (q--)
    {
        in(a), in(b);
        a = (a + lastans) % dot + 1, b = (b + lastans) % dot + 1;
        printf("%d\n", query(a, b));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值