• BZOJ 2821 分块+二分


    题意:
    N个数,M组询问,每次问[l,r]中有多少个数出现正偶数次。
    思路:
    把N个数分成sqrt(n)块,预处理d[i][j]表示第i块起点到第j块末尾的答案
    枚举起点i,并维护一个数组记录每个数到目前为止出现的次数,从偶变奇、从奇变偶时相应增减答案。
    把每个数在数列中出现的位置从小到大排序后放入到一个数组Arr中备用。
    读入每个询问[l,r]。如果l和r在同一个块中暴力即可,否则设l所在块的末尾为l’,r所在块的起点为r’,[l’+1,r’-1]的答案已经预处理出。扫描l~l’, r’~r的所有数,统计每个数出现的次数cnt,第一次出现时把它加入队列。
    对于队列中的每个数,在数组Arr中二分l’+1和r’-1,得到在[l’+1,r’-1]中出现的次数k。通过对k和当前队列中的数的cnt进行奇偶性讨论更新答案

    (from lyd的题解…)

    我们就可以vector +lower_bound()
    搞定~

    (也可以用可持久化线段树之类的东西…)

    最后  祝他们幸福……

    //By SiriusRen
    #include <cmath>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int N=100005;
    int n,c,m,a[N],Block,block[N],f[1111][1111],vis[N],ans,l,r,stk[N],top;
    vector<int>vec[N];
    int main(){
        scanf("%d%d%d",&n,&c,&m),Block=sqrt(n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),block[i]=(i-1)/Block+1,vec[a[i]].push_back(i);
        for(int i=1;i<=block[n];i++){
            memset(vis,0,sizeof(vis));int temp=0;
            for(int j=lower_bound(block,block+1+n,i)-block;j<=n;j++){
                vis[a[j]]++;
                if(vis[a[j]]%2==0)temp++;
                else if(vis[a[j]]!=1)temp--;
                if(block[j]!=block[j+1])f[i][block[j]]=temp;
            }
        }
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++){
            scanf("%d%d",&l,&r);
            l=(l+ans)%n+1,r=(r+ans)%n+1;
            if(l>r)swap(l,r);ans=0;
            int L=block[l]+1,R=block[r];
            if(L<R){
                int ll=lower_bound(block+1,block+1+n,L)-block-1;
                int rr=lower_bound(block+1,block+1+n,R)-block;
                R--;top=0;
                for(int i=l;i<=ll;i++)vis[a[i]]++,stk[++top]=a[i];
                for(int i=rr;i<=r;i++)vis[a[i]]++,stk[++top]=a[i];
                ans=f[L][R];
                for(int i=1;i<=top;i++)if(vis[stk[i]]){
                    int t=lower_bound(vec[stk[i]].begin(),vec[stk[i]].end(),rr)-
                    lower_bound(vec[stk[i]].begin(),vec[stk[i]].end(),ll+1);
                    if(!t){if(vis[stk[i]]%2==0)ans++;}
                    else if(t%2==0){if(vis[stk[i]]%2==1)ans--;}
                    else if(t%2==1&&vis[stk[i]]%2==1)ans++;
                    vis[stk[i]]=0;
                }
            }
            else{
                top=0;
                for(int i=l;i<=r;i++)vis[a[i]]++;
                for(int i=l;i<=r;i++)if(vis[a[i]]){
                    if(vis[a[i]]%2==0)ans++;
                    vis[a[i]]=0;
                }
            }
            printf("%d
    ",ans);
        }
    }

    这里写图片描述

  • 相关阅读:
    魏新 20190912-1 每周例行报告
    魏新 20190912-2 命令行
    魏新 20180912-3 词频统计
    魏新 20190905-1 每周例行报告
    魏新 20190905-3 命令行和控制台编程
    魏新 20190905-2 博客作业
    20190911-例行报告
    肖亚男 20190910-2 博客作业
    宋晓丽20190919-5 代码规范,结对要求
    宋晓丽20190919-3 效能分析
  • 原文地址:https://www.cnblogs.com/SiriusRen/p/6532036.html
Copyright © 2020-2023  润新知