【分块】BZOJ2821 作诗(Poetize)

2821: 作诗(Poetize)

Time Limit: 50 Sec  Memory Limit: 128 MB
Submit: 3265  Solved: 951
[Submit][Status][Discuss]

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

题解

分块,每块大小为sqrt(n/logn)

维护两个块内的ans

对于查询操作:

若并未经过完整的块,暴力求解

若经过了完整的块,二分不在块内的至多(2*块的大小)个元素的值在询问区间内出现的次数和在询问区间内的整块中出现的次数

判断对答案的影响

代码

//by 减维
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<map>
#include<bitset>
#include<algorithm>
#define ll long long
#define maxn 100005
#define inf 1<<30
using namespace std;

struct node{int v,p;}b[maxn];

int n,c,m,blo,a[maxn],pos[maxn],tmp[maxn],fir[maxn],las[maxn],mark[maxn];
int f[1505][1505],L[1505],R[1505];

bool cmp(const node&x,const node&y)
{
    if(x.v==y.v)return x.p<y.p;
    return x.v<y.v;
}

void pre()
{
    int tot;
    for(int i=1;i<=pos[n];++i)
    {
        for(int j=L[i];j<=n;++j)tmp[a[j]]=0;
        tot=0;
        for(int j=L[i];j<=n;++j)
        {
            if(tmp[a[j]]%2==0&&tmp[a[j]]!=0)tot--;
            tmp[a[j]]++;
            if(tmp[a[j]]%2==0&&tmp[a[j]]!=0)tot++;
            f[i][pos[j]]=tot;
        }
    }
    for(int i=1;i<=n;++i)b[i].v=a[i],b[i].p=i;
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;++i){
        if(!fir[b[i].v])fir[b[i].v]=i;
        las[b[i].v]=i;
    }
}

int findup(int x,int val)
{
    int l=fir[val],r=las[val];
    int tmp=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(x<b[mid].p)r=mid-1;
        else l=mid+1,tmp=mid;
    }
    return tmp;
}

int finddown(int x,int val)
{
    int l=fir[val],r=las[val];
    int tmp=inf;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(x>b[mid].p)l=mid+1;
        else r=mid-1,tmp=mid;
    }
    return tmp;
}

int find(int l,int r,int val)
{
    return max(findup(r,val)-finddown(l,val)+1,0);
}

int query(int x,int y)
{
    int ans=0,a1,t1,t2;
    if(pos[x]==pos[y]||pos[x]+1==pos[y])
    {
        for(int i=x;i<=y;++i)mark[a[i]]++;
        for(int i=x;i<=y;++i)
        {
            if(mark[a[i]]%2==0&&mark[a[i]]!=0)ans++;
            mark[a[i]]=0;
        }
        return ans;
    }
    int l=L[pos[x]+1],r=R[pos[y]-1];
    ans=f[pos[x]+1][pos[y]-1];
    for(int i=x;i<=y;++i)
    {
        if(i==l)i=r+1;
        a1=a[i];if(mark[a1])continue;
        t1=find(x,y,a1),t2=find(l,r,a1);
        if(t1%2==0){
            if(t2%2!=0||t2==0)ans++;
        }else if(t1%2==1){
            if(t2%2==0&&t2!=0)ans--;
        }
        mark[a1]=1;
    }
    for(int i=x;i<l;++i)mark[a[i]]=0;
    for(int i=r+1;i<=y;++i)mark[a[i]]=0;
    return ans;
}

int main()
{
    scanf("%d%d%d",&n,&c,&m);
    blo=sqrt(n/log2(n));
    for(int i=1;i<=n;++i)scanf("%d",&a[i]),pos[i]=(i-1)/blo+1;
    for(int i=1;i<=pos[n];++i)L[i]=(i-1)*blo+1,R[i]=i*blo;
    R[pos[n]]=n;
    pre();
    int ans=0;
    for(int i=1,x,y;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        x=(x+ans)%n+1,y=(y+ans)%n+1;
        if(x>y)swap(x,y);
        ans=query(x,y);
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/rir1715/p/8136618.html