普通线段树的叶子节点(最下面一层,从左到右的编号i依次是1,2,3..n)记录的是a[i],即给定的序列值

权值线段树的叶子节点i对应的cnt[i]记录的是序列去重后第i小的数出现的次数,对于给定的序列需要离散化确定大小

如序列:[1,1,2,3,3,4,4,4,4,5],对应的权值线段树为:

图中第二层的9表示序列中的前9小的数都在上一个节点的左子树,1表示第9+1-第10小的数都在上一个节点的右子树,其他同理。

(图源https://blog.csdn.net/Stupid_Turtle/article/details/80445998


权值线段树法:(整体二分查找)

测试样例:

7
3 3 2 1 5 5 7

#include <bits/stdc++.h>
using namespace std;
const int maxn = 300005;
int a[maxn], b[maxn], cnt[maxn];
void update(int id )
{
    cnt[id] = cnt[id << 1] + cnt[id << 1 | 1];
}
void build(int l, int r, int id, int no)
{
    if(l == r)
    {
        cnt[id]++;
        return ;
    }
    int mid = (l + r) >> 1;
    if(no <= mid) build(l, mid, id << 1, no);// 单点
    else build(mid + 1, r, id << 1 | 1, no);
    update(id);
}
int query(int l, int r, int id, int k)
{
    if(l == r)
    {
        return l; //返回1-n的第k大的数是去重之后的第几大数
    }
    int mid = (l + r) >> 1;
    if(k <= cnt[id << 1])
        query(l, mid, id << 1, k);
    else query(mid + 1, r, id << 1 | 1, k - cnt[id << 1]);//!k - cnt[id << 1]
}
int main()
{
    freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[i] = a[i];
    }
    sort(a + 1, a + 1 + n);
    int m = unique(a+1, a+1+n) - (a+1);
    for(int i = 1; i <= n; i++)
    {
        int no = lower_bound(a+1, a+1+m, b[i]) - a; //去重后第几大
        build(1, n, 1, no);
    }
    for(int i = 1; i <= n; i++)
    {
        int id = query(1, n, 1, i);
        cout << a[id] << endl;
    }

    return 0;
}

离散化法:直接copy过来了,懒得改了。

#include <iostream>
#include <string.h>
#include <map>
#define maxn 1005
using namespace std;
struct node{
    int x,id;//id表示输出时的顺序
    friend bool operator <(node a,node b)
    {
        return a.x<b.x;//升序
    }
}a[maxn],b[maxn];
int main()
{
    int n,rank[maxn];
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].x);
        a[i].id=i;
    }
    //可以用b数组存初始的a数组
    /*memcpy(b,a,sizeof(a));
    for(int i=1;i<=n;i++)
        cout<<b[i].x<<" ";
    cout<<endl;*/
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    {
        rank[a[i].id]=i;
        cout<<a[i].x<<" "<<i<<endl;
    }
    return 0;
}