zoukankan      html  css  js  c++  java
  • [BZOJ 1878][SDOI2009]HH的项链

    Link:

    BZOJ 1878 传送门

    Solution:

    很久以前这道题我是用$BIT$过的

    思路非常简单:离线将询问排序,记录每个点同一颜色的后继节点

    随着询问左边界的递增,将处于两次左边界间的节点从$BIT$中删去,而加入其后继节点的位置

    接下来统计处于$[l,r]$间的数的个数就好了

    同时这道题也是莫队算法的经典适用情况:离线的区间问题

    先安利一篇博客:传送门 果然女队的文章写得就是清楚啊

    感觉莫队算法实际上就是离线问题中将询问按一定方式排序后的优雅暴力

    我们将$n$个点分为$sqrt(n)$块,所有询问按以下规则排序:

    1、左边界所在块的编号为第一关键字

    2、右边界的位置为第二关键字

    按这样的顺序暴力移动左右指针处理每个询问就能将复杂度控制在$O(n*log(n))$了!

    简易证明:

    1、对于左指针,每次询问最多移动块长度$sqrt(n)$,因此总复杂度为$O(n*sqrt(n))$

    2、对于右指针,当左指针在同一块时有序,最多移动$n$,而只有$sqrt(n)$块,因此总复杂度也为$O(n*sqrt(n))$

    通过构造让左右指针“互相迁就”的方式使复杂度符合了要求

    可能是现在看到最优雅的算法之一?看来我还是适合暴力啊

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef pair<int,int> P;
    typedef pair<P,int> PP;
    #define F first
    #define S second
    
    const int MAXN=1000010;
    int dat[MAXN],bit[MAXN],res[MAXN],nxt[MAXN],cur[MAXN],n,c,m;
    PP op[MAXN];
    
    inline int read()
    {
        char ch;int num,f=0;
        while(!isdigit(ch=getchar())) f|=(ch=='-');
        num=ch-'0';
        while(isdigit(ch=getchar())) num=num*10+ch-'0';
        return f?-num:num;
    }
    
    inline void write(long long x)
    {
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    
    void update(int pos,int x)
    {
        while(pos<=n)
        {
            bit[pos]+=x;
            pos+=pos&(-pos);
        }
    }
    
    int cal(int pos)
    {
        int ret=0;
        while(pos)
        {
            ret+=bit[pos];
            pos-=pos&(-pos);
        }
        return ret;
    }
    
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) dat[i]=read();
        m=read();
        for(int i=1;i<=m;i++) op[i].F.F=read(),op[i].F.S=read(),op[i].S=i;
        
        fill(nxt,nxt+MAXN,MAXN-2);
        for(int i=1;i<=n;i++)
        {
            if(!cur[dat[i]]) update(i,1);
            nxt[cur[dat[i]]]=i,cur[dat[i]]=i;
        }
        
        op[0].F.F=1;
        sort(op+1,op+m+1);
        
        for(int i=1;i<=m;i++)
        {
            for(int j=op[i-1].F.F;j<op[i].F.F;j++)
                update(j,-1),update(nxt[j],1);
            res[op[i].S]=cal(op[i].F.S)-cal(op[i].F.F-1);
        }
        
        for(int i=1;i<=m;i++) write(res[i]),putchar('
    ');
        return 0;
    }
    BIT
    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MAXN=1e6+10;
    int n,m,k,blk,dat[MAXN],cnt[MAXN],res[MAXN],sum=0;
    struct Query{int l,r,id;}qry[MAXN];
    
    int cal(int x){return (x-1)/blk+1;}
    bool cmp(Query x,Query y)
    {return (cal(x.l)!=cal(y.l))?x.l<y.l:x.r<y.r;}
    
    void update(int pos,int k)
    {
        int pre=cnt[dat[pos]];cnt[dat[pos]]+=k;
        if(!pre&&cnt[dat[pos]]) sum++;
        if(pre&&!cnt[dat[pos]]) sum--;
    }
    
    int main()
    {
        scanf("%d",&n);blk=sqrt(n);
        for(int i=1;i<=n;i++) scanf("%d",&dat[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&qry[i].l,&qry[i].r),qry[i].id=i;
        sort(qry+1,qry+m+1,cmp);
        
        int l=1,r=0;
        for(int i=1;i<=m;i++)
        {
            while(l<qry[i].l) update(l++,-1);
            while(r>qry[i].r) update(r--,-1);        
            while(l>qry[i].l) update(--l,1);
            while(r<qry[i].r) update(++r,1);
            res[qry[i].id]=sum;
        }
        
        for(int i=1;i<=m;i++) printf("%d
    ",res[i]);
        return 0;
    }
    莫队
  • 相关阅读:
    MongoDB创建数据库并授权
    nginx反向代理tomcat后,登录提示验证码错误,直接通过ip访问tomcat服务就可以
    Centos7 为es安装kibana
    ssh登录很慢解决方法
    nginx反向代理后端服务,访问时页面空白
    Error: rpmdb open failed
    centos7虚拟内存设置
    CentOS7利用docker安装MySQL5.7
    uniapp实现顶部tab选项卡
    top使用方法总结
  • 原文地址:https://www.cnblogs.com/newera/p/9363157.html
Copyright © 2011-2022 走看看