模板:主席树

求区间不同数的个数:

/*
5
1 1 2 1 3
3
1 5 (3)
2 4 (2)
3 5 (3)
*/
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
const int maxn=2e5+10;
const int maxm=1e6+10;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int n, q;
int cnt;
struct node{
    int l,r;
    int sum;
}t[maxn * 20];

int root[maxn];
int last[maxm],pre[maxn];

void update(int pos,int &rt,int l,int r){
    t[++cnt]=t[rt];
    rt=cnt;
    t[rt].sum++;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(pos<=mid) update(pos,t[rt].l,l,mid);
    else update(pos,t[rt].r,mid+1,r);
}
int query(int x,int y,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr) return t[y].sum-t[x].sum;
    else{
        int mid=(l+r)>>1,ans=0;
        if(ql<=mid) ans+=query(t[x].l,t[y].l,l,mid,ql,qr);
        if(mid<qr)  ans+=query(t[x].r,t[y].r,mid+1,r,ql,qr);
        return ans;
    }
}

int main(){
    n=read();
    for(int i=1;i<=n;i++){
        int x=read();
        pre[i]=last[x];
        last[x]=i;
    }
    for(int i=1;i<=n;i++){
        root[i]=root[i-1];
        update(pre[i],root[i],0,n);
    }
    q=read();
    while(q--){
        int ql=read(),qr=read();
        printf("%d\n",query(root[ql-1],root[qr],0,n,0,ql-1));
    }
    return 0;
}

求区间第k小:

/*
7 3
1 5 2 6 3 7 4
2 5 3 (5)
4 4 1 (6)
1 7 3 (3)
*/
#include<bits/stdc++.h>
using namespace std;  
const int maxn = 1e5 + 10;  
int n, m;  
int cnt;  
struct node{  
    int L, R;//分别指向左右子树  
    int sum;//该区间内有多少个数  
}Tree[maxn * 20];  
struct value{  
    int x;//值的大小  
    int id;//离散之前在原数组中的位置  
}Value[maxn];  
bool cmp(value v1, value v2)  
{  
    return v1.x < v2.x;  
}  
int root[maxn];//多颗线段树的根节点  
int arr[maxn];//原数组离散之后的数组  
  
void update(int num, int &rt, int l, int r)  
{  
    Tree[cnt++] = Tree[rt];  
    rt = cnt - 1;  
    Tree[rt].sum++;  
    if(l == r) return;  
    int mid = (l + r)>>1;  
    if(num <= mid) update(num, Tree[rt].L, l, mid);  
    else update(num, Tree[rt].R, mid + 1, r);  
}  
int query(int i, int j, int k, int l, int r)  
{  
    int d = Tree[Tree[j].L].sum - Tree[Tree[i].L].sum;  
    if(l == r) return l;  
    int mid = (l + r)>>1;  
    if(k <= d) return query(Tree[i].L, Tree[j].L, k, l, mid);  
    else return query(Tree[i].R, Tree[j].R, k - d, mid + 1, r);  
}  
int main()  
{  
    scanf("%d%d", &n, &m);  
    for(int i = 1; i <= n; i++)  
    {  
        scanf("%d", &Value[i].x);  
        Value[i].id = i;  
    }  
    //进行离散化  
    sort(Value + 1, Value + n + 1, cmp);  
    for(int i = 1; i <= n; i++)  
    {  
        arr[Value[i].id] = i;  
    }  
    cnt=1;  
    for(int i = 1; i <= n; i++)  
    {  
        root[i] = root[i - 1];  
        update(arr[i], root[i], 1, n);  
    }  
    int left, right, k;  
    for(int i = 1; i <= m; i++)  
    {  
        scanf("%d%d%d", &left, &right, &k);  
        printf("%d\n", Value[query(root[left - 1], root[right], k, 1, n)].x);  
    }  
    return 0;  
}  

求带修改的区间第k小:

/*
5 3
3 2 1 4 7
Q 1 4 3 (3)
C 2 6
Q 2 5 3 (6)
*/
#include<bits/stdc++.h>
using namespace std;  
int use[maxn*10],n,m,size,tot=0,all=0,h[maxn*10],v[maxn*10],t[maxn*10];  

struct chairtree{  
    int l,r,size;  
}a[maxn*300];  
struct question{  
    int l,r,k;  
}q[maxn];
  
int lowbit(int x){  
    return x&(-x);  
}  

void Hash1(){  
    sort(h+1,h+1+all);  
    size=unique(h+1,h+1+all)-h-1;  
}  
int Hash(int x){  
    return lower_bound(h+1,h+1+size,x)-h;  
}  

int Build(int l,int r){  
    int now=++tot;  
    a[now].size=0;  
    if (l==r) return now;  
    int m=(l+r)>>1;  
    a[now].l=Build(l,m);  
    a[now].r=Build(m+1,r);  
    return now;  
}  
int Update(int root,int p,int val){  
    int now=++tot,tmp=now;  
    int l=1,r=size;  
    a[now].size=a[root].size+val;  
    while (l<r){  
        int m=(l+r)>>1;  
        if (p<=m){  
            a[now].l=++tot;  
            a[now].r=a[root].r;  
            root=a[root].l;  
            now=a[now].l;  
            r=m;  
        }  
        else{  
            a[now].l=a[root].l;  
            a[now].r=++tot;  
            root=a[root].r;  
            now=a[now].r;  
            l=m+1;  
        }  
        a[now].size=a[root].size+val;  
    }  
    return tmp;  
}  

void Add(int x,int p,int val){  
    for (int i=x;i<=n;i+=lowbit(i))    //树状数组只需修改logn个  
        t[i]=Update(t[i],p,val);  
}  
int Getsum(int x){  
    int ans=0;  
    for (int i=x;i;i-=lowbit(i))  
        ans+=a[a[use[i]].l].size;  
    return ans;  
}  
int Query(int lx,int rx,int k){  
    int l=1,r=size;  
    for (int i=lx-1;i;i-=lowbit(i)) use[i]=t[i];  
    for (int i=rx;i;i-=lowbit(i)) use[i]=t[i];  
    while (l<r){  
        int m=(l+r)>>1;  
        int tmp=Getsum(rx)-Getsum(lx-1);  
        if (tmp>=k){  
            for (int i=lx-1;i;i-=lowbit(i))   
                use[i]=a[use[i]].l;  
            for (int i=rx;i;i-=lowbit(i))   
                use[i]=a[use[i]].l;  
            r=m;  
        }  
        else{  
            for (int i=lx-1;i;i-=lowbit(i))  
                use[i]=a[use[i]].r;  
            for (int i=rx;i;i-=lowbit(i))  
                use[i]=a[use[i]].r;  
            k-=tmp;  
            l=m+1;  
        }  
    }  
    return l;  
}  
int main(){  
    scanf("%d%d",&n,&m);  
    for (int i=1;i<=n;i++)  
    scanf("%d",&v[i]),h[i]=v[i];  
    all=n;  
    for (int i=1;i<=m;i++){  
        char str[10];  
        int l,r,k;  
        scanf("%s",str);  
        if (str[0]=='Q'){  
            scanf("%d%d%d",&l,&r,&k);  
            q[i].l=l,q[i].r=r,q[i].k=k;  
        }  
        else{  
            scanf("%d%d",&r,&k);  
            q[i].l=0,q[i].r=r,q[i].k=k;  
                        h[++all]=k;  
        }  
    }  
    Hash1();  
    t[0]=Build(1,size);  
    for (int i=1;i<=n;i++)  
        t[i]=t[0];  
    for (int i=1;i<=n;i++)  
        Add(i,Hash(v[i]),1);  
    for (int i=1;i<=m;i++){  
        if (q[i].l)  
            printf("%d\n",h[Query(q[i].l,q[i].r,q[i].k)]);  
        else{  
            Add(q[i].r,Hash(v[q[i].r]),-1);  
            Add(q[i].r,Hash(q[i].k),1);  
            v[q[i].r]=q[i].k;  
        }  
    }  
    return 0;  
}  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值