普通线段树的叶子节点(最下面一层,从左到右的编号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;
}