刷题总结——分块算法(bzoj2821作诗)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题总结——分块算法(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次能选出的汉字的最多种类数。

 

Sample Input

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

Sample Output

2
0
0
0
1

HINT

 

对于100%的数据,1<=n,c,m<=10^5

 

Source

题解:

不得不吐槽这个题目太啰嗦了····给最后一句话就可以了·····

和之前算法复习里的例题几乎是一样的,预处理cnt[i][j]表示数i在前j个块中的数量,ans[i][j]为第i个块到第j个块的答案·····然后就是分块的套路了···详细见代码···

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e5+5;
const int M=320;
int tots,s,sum[N][M],ans[M][M],n,c,m,num[N],id[N],Left[M],Right[M],tot[N],tim,visit[N];
bool jud[N];
inline int R()
{
  char c;int f=0;
  for(c=getchar();c<0||c>9;c=getchar());
  for(;c<=9&&c>=0;c=getchar())
    f=(f<<3)+(f<<1)+c-0;
  return f;
}
inline void pre()
{
  s=(int)sqrt(n);tots=0;
  for(int i=1;i<=n;i++)
  {
    if(i%s==1)  id[i]=++tots,Left[tots]=i;
    else if(i%s==0)  id[i]=tots,jud[i]=true,Right[tots]=i;
    else id[i]=tots;
  }
  Right[tots]=n,jud[n]=true;
  
  for(int i=1;i<=n;i++)
    sum[num[i]][id[i]]++;
  for(int i=2;i<=tots;i++)
    for(int j=1;j<=c;j++)
      sum[j][i]+=sum[j][i-1];
  
  tim=0;
  for(int i=1;i<=tots;i++)
  {
    int cn=0;tim++;
    for(int j=Left[i];j<=n;j++)
    {
      if(visit[num[j]]!=tim)  visit[num[j]]=tim,tot[num[j]]=1;
      else tot[num[j]]++;
      
      if(tot[num[j]]%2==0)  cn++;
      else if(tot[num[j]]!=1)  cn--;
    
      if(jud[j]==true)  ans[i][id[j]]=cn;
    }
  }
  memset(visit,0,sizeof(visit));
}
inline int getans(int a,int b)
{
  if(b-a+1<2*s)
  {
    tim++;
    int cn=0;
    for(int i=a;i<=b;i++)
    {
      if(visit[num[i]]!=tim)  visit[num[i]]=tim,tot[num[i]]=1;
      else tot[num[i]]++;
      if(tot[num[i]]%2==0) cn++; 
      else if(tot[num[i]]!=1)  cn--;
    }
    return cn;
  }
  else
  {
    tim++;
    int lefts,rights; 
    if(a%s==1)  lefts=id[a];
    else lefts=id[a]+1;
    if(b%s==0)  rights=id[b];
    else rights=id[b]-1;
    int cn=ans[lefts][rights];
    for(int i=a;i<Left[lefts];i++)
    {
      if(visit[num[i]]!=tim)  visit[num[i]]=tim,tot[num[i]]=1;
      else tot[num[i]]++;
      
      if((tot[num[i]]+sum[num[i]][rights]-sum[num[i]][lefts-1])%2==0)  cn++;
      else if(tot[num[i]]==1&&sum[num[i]][rights]-sum[num[i]][lefts-1]==0)  continue;
      else cn--;
    }
    for(int i=Right[rights]+1;i<=b;i++)
    {
      if(visit[num[i]]!=tim)  visit[num[i]]=tim,tot[num[i]]=1;
      else tot[num[i]]++;
      
      if((tot[num[i]]+sum[num[i]][rights]-sum[num[i]][lefts-1])%2==0)  cn++;
      else if(tot[num[i]]==1&&sum[num[i]][rights]-sum[num[i]][lefts-1]==0)  continue;
      else cn--;
    }
    return cn;
  }
}
int main()
{
  //freopen("a.in","r",stdin);
  n=R(),c=R(),m=R(); 
  for(int i=1;i<=n;i++)
    num[i]=R();
  pre();
  int a,b,t=0;
  while(m--)
  {
    a=R(),b=R();
    a=(a+t)%n+1;b=(b+t)%n+1;
    if(a>b)  swap(a,b);
    t=getans(a,b);
    printf("%d\n",t);
  }
  return 0;
}

 











以上是关于刷题总结——分块算法(bzoj2821作诗)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2821作诗(Poetize) 分块

BZOJ 2821: 作诗(Poetize) | 分块

BZOJ 2821作诗(Poetize) 分块

bzoj2821 作诗(Poetize)

BZOJ2821: 作诗(Poetize)

[BZOJ2821]作诗(Poetize)