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

    题意:

    链接:

    https://www.luogu.org/problemnew/show/1972

    题解:

    很好的模板题。。。。

    1.主席树

    为避免将一个元素重复计算,我们可以保证只计算一个区间内该元素的最后一个

    在操作时我们只需要将这个位置的线段树的这个元素的下一个出现的位置的值++(有点绕)

    之后就只需要查询在区间内大于r的元素的个数,这很显然就是一个主席树的板题了

    细节:对于每种元素的最后一个可以将其插入在n+1处

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 2000000
    #define mid (h+t)/2
    int f[maxn],a[maxn],pos[maxn],fa[maxn],l;
    struct ree{int a,b;}b[maxn];
    struct re{int h,t,x;}p[maxn];
    void build(int x,int h,int t)
    {
        l=max(l,x);
        if (h==t) return;
        p[x].h=x*2; p[x].t=x*2+1;
        build(x*2,h,mid); build(x*2+1,mid+1,t);
    }
    void insert(int x,int &y,int goal,int h,int t)
    {
        y=++l;
        p[y]=p[x];  p[y].x++;
        if (h==t) return;
        if (goal<=mid) insert(p[x].h,p[y].h,goal,h,mid);
        else insert(p[x].t,p[y].t,goal,mid+1,t);
    }
    int query(int x,int y,int h,int t)
    {
        if (t<=y) return(0);
        if (h>y) return(p[x].x);
        return(query(p[x].h,y,h,mid)+query(p[x].t,y,mid+1,t));
    }
    bool cmp(ree x,ree y)
    {
        return(x.b<y.b);
    }
    int main(){
        freopen("noip.in","r",stdin);
        freopen("noip.out","w",stdout);
      int n,m,c,d;
      cin>>n;
      for (int i=1;i<=n;i++) 
          b[i].a=i,cin>>b[i].b;
        sort(b+1,b+n+1,cmp);
        b[0].b=99999999; int o=0;
        for (int i=1;i<=n;i++)
        {
          if (b[i].b!=b[i-1].b) o++;
          a[b[i].a]=o;
      }
        for (int i=n;i>=1;i--)
        {
            if (f[a[i]]==0) pos[i]=n+1;
            else pos[i]=f[a[i]];
            f[a[i]]=i;
        }
        n+=2;
        build(1,1,n);
        fa[0]=1;
        for (int i=1;i<=n;i++)
        {
            insert(fa[i-1],fa[i],pos[i],1,n);
        }
        cin>>m;
        for (int i=1;i<=m;i++)
        {
            cin>>c>>d;
            cout<<(query(fa[d],d,1,n)-query(fa[c-1],d,1,n))<<endl;
        }
    }
    View Code

    2.离线+树状数组

    对于若干个询问的区间[l,r],如果他们的r都相等的话,那么项链中出现的同一个数字,一定是只关心出现在最右边的那一个的

    所以我们可以首先将区间按照r排序

    这样有什么好处呢,如果一个元素重复出现的话,之前的那个位置上的就再也不可能有用了

    所以可以用树状数组来维护,如果一个元素重复出现,sum【现在的位置】++,sum【原先的位置】--;

    统计的时候只需要计算sum[i]-sum[j-1]即可

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 2000000
    int a[maxn],f[maxn],p[maxn],c[maxn],n,m;
    struct re{int a,b,c;}b[maxn];
    bool cmp(re x,re y)
    {
        return(x.b<y.b);
    }
    int lowbit(int x)
    {
        return(x & -x);
    }
    void change(int x,int y)
    {
        while (x<=n)
        {  
          p[x]+=y;
          x+=lowbit(x);
      }
    }
    int query(int x)
    {
        int num=0;
        while (x>0)
        {
            num+=p[x];
            x-=lowbit(x);
        }
        return(num);
    }
    int main()
    {
        freopen("noip.in","r",stdin);
        freopen("noip3.out","w",stdout); 
        cin>>n; 
        for (int i=1;i<=n;i++)
          cin>>a[i];
        cin>>m;
        for (int i=1;i<=m;i++)
          cin>>b[i].a>>b[i].b,b[i].c=i;
        sort(b+1,b+m+1,cmp);
        int now=1;
        for (int i=1;i<=m;i++)
        {
            for (int j=now;j<=b[i].b;j++)
            {
              if (f[a[j]]>0) change(f[a[j]],-1);
              change(j,1);
              f[a[j]]=j;
          }
          now=b[i].b+1;
          c[b[i].c]=query(b[i].b)-query(b[i].a-1);
        }
        for (int i=1;i<=m;i++)
          cout<<c[i]<<endl;
    }
    View Code

    3.莫队

    最暴力的区间解法,裸的莫队

  • 相关阅读:
    20169215 缓冲区溢出漏洞实验
    20169215 2016-2017-2 实验二Nmap的使用与分析
    20169215 2016-2017-2 《网络攻防实践》/《网络攻击与防范》第八周学习总结
    Numpy Usage Introduction
    [Example of Sklearn]
    [Example of Sklearn]
    [Example of Sklearn]
    [Scikit-Learn]
    [Scikit-Learn]
    [Scikit-Learn]
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/8166946.html
Copyright © 2011-2022 走看看