bzoj2821 作诗

Description 神犇SJY虐完HEOI之后给傻×LYD出了一题:SHY是T国的公主,平时的一大爱好是作诗。由于时间紧迫,SHY作完诗
之后还要虐OI,于是SHY找来一篇长度为N的文章,阅读M次,每次只阅读其中连续的一段[l,r],从这一段中选出一
些汉字构成诗。因为SHY喜欢对偶,所以SHY规定最后选出的每个汉字都必须在[l,r]里出现了正偶数次。而且SHY认
为选出的汉字的种类数(两个一样的汉字称为同一种)越多越好(为了拿到更多的素材!)。于是SHY请LYD安排选
法。LYD这种傻×当然不会了,于是向你请教……问题简述:N个数,M组询问,每次问[l,r]中有多少个数出现正偶 数次。

Input 输入第一行三个整数n、c以及m。表示文章字数、汉字的种类数、要选择M次。第二行有n个整数,每个数Ai在[1, c
]间,代表一个编码为Ai的汉字。接下来m行每行两个整数l和r,设上一个询问的答案为ans(第一个询问时ans=0),
令L=(l+ans)mod n+1, R=(r+ans)mod n+1,若L>R,交换L和R,则本次询问为[L,R]。

Output

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

分块。
首先预处理出任意连续的几段中出现偶数次的数字个数,以及每个数在序列中出现的位置下标。
对于在非整块中出现的数暴力统计,结合在整块中出现的次数讨论得出答案。

#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
int a[100010],L[10010],R[10010],sta[100010],
tot[5010][5010],cnt[100010],last[100010],m,n,T;
vector<int> plc[100010];
vector<int>::iterator i1,i2;
int num(int x,int l,int r)
{
    i1=lower_bound(plc[x].begin(),plc[x].end(),l);
    i2=upper_bound(plc[x].begin(),plc[x].end(),r);
    return i2-i1;
}
int rd()
{
    int x=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return x;
}
int main()
{
    int c,i,j,k,p,q,x,y,z,ans,l,r,ll,rr,K,top,size;
    scanf("%d%d%d",&n,&c,&m);
    for (i=1;i<=n;i++)
    {
        a[i]=rd();
        plc[a[i]].push_back(i);
    }
    size=sqrt((double)n/log(n)*log(2));
    if (n%size) T=n/size+1;
    else T=n/size;
    for (i=1;i<=T;i++)
    {
        L[i]=R[i-1]+1;
        R[i]=i==T?n:L[i]+size-1;
    }
    for (i=1;i<=T;i++)
    {
        for (j=i;j<=T;j++)
        {
            tot[i][j]=tot[i][j-1];
            for (k=L[j];k<=R[j];k++)
            {
                if (last[a[k]]<i)
                {
                    last[a[k]]=i;
                    cnt[a[k]]=1;
                }
                else cnt[a[k]]++;
                if (cnt[a[k]]&1^1) tot[i][j]++;
                else
                {
                    if (cnt[a[k]]>1) tot[i][j]--;
                }
            }
        }
    }
    ans=0;
    memset(last,0,sizeof(last));
    for (K=1;K<=m;K++)
    {
        l=rd();
        r=rd();
        l=(l+ans)%n+1;
        r=(r+ans)%n+1;
        if (l>r) swap(l,r);
        ll=1;
        while (L[ll]<l&&ll<=T) ll++;
        rr=T;
        while (R[rr]>r&&rr) rr--;
        if (ll>rr)
        {
            ans=0;
            for (i=l;i<=r;i++)
              if (last[a[i]]<K)
              {
                last[a[i]]=K;
                if (num(a[i],l,r)&1^1) ans++;
              }
            printf("%d\n",ans);
            continue;
        }
        ans=tot[ll][rr];
        top=0;
        for (i=l;i<L[ll];i++)
          if (last[a[i]]<K)
          {
            last[a[i]]=K;
            sta[++top]=a[i];
          }
        for (i=R[rr]+1;i<=r;i++)
          if (last[a[i]]<K)
          {
            last[a[i]]=K;
            sta[++top]=a[i];
          }
        for (i=1;i<=top;i++)
        {
            x=num(sta[i],L[ll],R[rr]);
            if (!x||(x&1))
            {
                if (num(sta[i],l,r)&1^1) ans++;
            }
            else
            {
                if (num(sta[i],l,r)&1) ans--;
            }
        }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值