zoukankan      html  css  js  c++  java
  • 【权值线段树】

    学习权值线段树,首先要了解线段树是什么。如果不会的可以先学习一下。

    是什么
    权值线段树,顾名思义是一棵线段树。
    但它和普通线段树不同:
    线段树,每个节点用来维护一段区间的最大值或总和等。
    权值线段树,相当于一个桶,每个节点用来表示一个区间的数***出现的次数***。

    为什么要用它
    我们可以用它来维护一段区间的数出现的次数,从它的定义上来看,它可以快速计算一段区间的数的出现次数。
    此外,它还有一个重要功能,在于它可以快速找到第k kk大或第k kk小值,下面会做详细解释。
    其实,它就是一个桶,桶能做到的它都可以用更快的速度去完成。

    基本操作
    添加
    和普通线段树类似,递归到叶子节点时给f[v]+1 f[v]+1f[v]+1。
    以下代码要添加的数是x xx,也就是x xx出现的次数+1 +1+1。

    一道简单模板题;

    也记记吧,免得脑子不灵光又忘记了。

    洛谷 1168

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=1e5+5;
    int n;
    int a[N],b[N];
    struct Tree
    {
    int l,r,mid;
    int num;
    }tree[N<<2];
    int read()
    {
    char c=getchar();int num=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
    num=num*10+c-'0';
    return num;
    }
    void build(int root,int l,int r)
    {
    tree[root].l=l,tree[root].r=r,tree[root].mid=l+r>>1;
    if(l==r)
    return;
    build(root<<1,l,tree[root].mid);
    build(root<<1|1,tree[root].mid+1,r);
    }
    void update(int root,int x)
    {
    ++tree[root].num;
    if(tree[root].l==tree[root].r)
    return;
    if(x<=tree[root].mid)
    update(root<<1,x);
    else
    update(root<<1|1,x);
    }
    int query(int root,int num)
    {
    if(tree[root].l==tree[root].r)
    return tree[root].l;
    if(num<=tree[root<<1].num)
    return(query(root<<1,num));
    else
    return(query(root<<1|1,num-tree[root<<1].num));
    }
    int main()
    {
    n=read();
    for(int i=1;i<=n;++i)
    a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1);
    int bound=unique(b+1,b+n+1)-b;
    build(1,1,n);
    for(int i=1;i<=n;++i){
    int pos=lower_bound(b+1,b+bound+1,a[i])-b;
    update(1,pos);
    if(i%2)
    printf("%d\n",b[query(1,i/2+1)]);
    }
    return 0;
    }

    hdu 6703 array

    题目链接http://acm.hdu.edu.cn/showproblem.php?pid=6703

    题目大意:

    给出一个n(n<1e5)个元素的数组A,A中所有元素都是不重复的[1,n]。

    有两种操作:

    1.将pos位置的元素+1e7

    2.查询不属于[1,r]中的最小的>=k的值。

    强制在线,上次计算结果和输入值xor得到区间。

    比赛的时候感觉这道题有点线段树的感觉,和前段时间多校训练一个题很像,想了40多分钟才理想清思路。

    解法:

      可以看出,执行1操作的时候加的数非常大,可以得出每次输出的答案在为1到n+1之间。可以将1-n的每个数在数组A中的位置记录下,存在线段树中,维护线段树区间最大值。

      当执行2操作时,只需要查询区间[k,n]中大于r的值即可,找出最小的数字,先搜索左子树,若无答案搜索右子树,若两侧均无解则答案为n+1。

      当执行1操作时,由于pos+1e7,因此之后的数组中不存在该数,将第pos位的数字在线段树中的值转换为inf即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxx=100005;
    const int inf=0x3f3f3f3f;
    int T,n,m,a[maxx],c[maxx],r,k,x,ans;
    struct node
    {
        int l,r,v;
    }Tr[maxx*4];
    void build(int id,int l,int r)
    {
        Tr[id].l=l,Tr[id].r=r;
        if(l==r){Tr[id].v=c[l];return;}
        int mid=(l+r)/2;
        build(id*2,l,mid);
        build(id*2+1,mid+1,r);
        Tr[id].v=max(Tr[id*2].v,Tr[id*2+1].v);
    }
    void update(int id,int num)
    {
        if(Tr[id].l==Tr[id].r) {Tr[id].v=inf;return;}
        int mid=(Tr[id].l+Tr[id].r)/2;
        if(num<=mid)update(id*2,num);
        else update((id*2+1),num);
        Tr[id].v=max(Tr[id*2].v,Tr[id*2+1].v);
    }
    int query(int id,int r,int k)
    {
        if(Tr[id].l==Tr[id].r){
            if(Tr[id].v>r) return Tr[id].l;
            else return -1;
        }
        int mid=(Tr[id].l+Tr[id].r)/2,ans=-1;
        if(mid>=k&&Tr[id*2].v>r) ans=query(id*2,r,k);
        if(ans!=-1) return ans;
        else if(Tr[id*2+1].r>=k&&Tr[id*2+1].v>r) ans=query(id*2+1,r,k);
        return ans;
    }
    int main()
    {
        scanf("%d",&T);
        while(T--){
            ans=0;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)scanf("%d",&a[i]),c[a[i]]=i;
            build(1,1,n);
            while(m--){
    
                scanf("%d",&x);
                if(x==2){scanf("%d %d",&r,&k);ans=query(1,r^ans,k^ans);if(ans==-1)ans=max(n+1,k);printf("%d\n",ans);}
                else{scanf("%d",&x);update(1,a[x^ans]);}
            }
        }
    }
    

      

  • 相关阅读:
    STM32的DMA
    STM32 入门之 GPIO (zhuan)
    CRC校验码 代码
    actan函数 查表法
    UART 和 USART 的区别
    STM32的NVIC理解
    STM32_adc
    STM 32 can 实例代码
    在Visual C#中调用API的基本过程
    贴片电阻阻值标识
  • 原文地址:https://www.cnblogs.com/hgangang/p/11518468.html
Copyright © 2011-2022 走看看