线段树离散化
https://blog.youkuaiyun.com/ling_wang/article/details/81707676
离散化转自此处。
离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。这是百度百科上的定义。那么举个栗子,某个题目告诉你有1e5个数,每个数大小不超过1e9,要你对这些数进行操作(比如并查集之类的)。那么肯定不能直接开1e9大小的数组,但是1e5的范围就完全没问题。在举个栗子,现在对{4,7,6,9}进行离散化,那么得到的结果是{1,3,2,4},也就是说,当我们并不需要这些数据具体是多少时,就只需要知道他们的相对大小就行了。
离散化有两种方法:
第一种, 先看一段代码:
const int N=1e5+7;
int t[N],a[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],t[i]=a[i];
sort(t+1,t+n+1);
m=unique(t+1,t+n+1)-t-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(t+1,t+m+1,a[i])-t;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在这段代码中,a[]经过离散,范围就变成了m。解释一下,unique是c++自带的一个函数,表示对一个数列去重,然后返回不重复的元素个数,当然在后面要减去首地址。那么这种离散化对于有重复元素的数列也可以适用,但复杂度相对后面要讲的第二种方法会高些。
举个栗子
{6,8,4,9,5,6,7,4},首先排序后得到{4,4,5,6,6,7,8,9},去重{4,5,6,7,8,9},然后原序列就变成了{3,5,1,6,2,3,4,1}。
第二种,复杂度比上面那一种要优,但不能处理重复元素。
先看代码:
const int N=1e5+7;
struct Node{
int v,id;
bool operator < (const Node a)const
{
return v<a.v;
}//排序用
}a[N];
int n,rank[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].v;
a[i].id=i;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) rank[a[i].id]=i;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这种方法直接用结构体存储原本的数列的元素的位置,然后排序以后将他们再重新赋值。那么rank[]就是结构体a[]离散化后的结果。
举个栗子:
v: 3 6 5 10 8
id:1 2 3 4 5
排序以后:
v: 3 5 6 8 10
id:1 3 2 5 4
所以离散化以后:
v: 3 5 6 8 10
id:1 3 2 5 4
rk:1 2 3 4 5
在按原来的顺序排列:
v: 3 6 5 10 8
rk:1 3 2 5 4
题目:
题目描述

输入
For each test case:
The first line contains two integers n and m — n represents the number of elemens in sequence W and m is as described above.
The second line contains n integers, which means the sequence W. 1 <= Q <= 15
1 <= n <= 2*105
1 <= m <= 109
For each i, 1 <= Wi <= m
输出

样例输入
复制样例数据
2
7 15
1 2 3 4 5 6 7
5 100
80 40 40 40 60
样例输出
0 0 0 0 0 2 3
0 1 1 2 3
#include <iostream>
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 2e5+100;
template<class T>
inline void read(T &ret) {
char c;
ret=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9') {
ret=ret*10+(c-'0'),c=getchar();
}
}
template<class T>
inline void print(T x){
if(x>9) {
print(x/10);
}
putchar(x%10+'0');
}
struct tree{
int l,r;
ll sum,num;
}T[4*maxn];
ll a[maxn];
ll n,m;
ll b[maxn];
int ans[maxn];
inline void build(int l,int r,int rt){
T[rt].l=l;
T[rt].r=r;
T[rt].sum=0;
T[rt].num=0;
if(l==r) return;
int mid=(l+r)/2;
build(l,mid,rt*2);
build(mid+1,r,rt*2+1);
}
inline void update(int pos,int p){
T[pos].num++;
if(T[pos].l==T[pos].r&&T[pos].r==p){
T[pos].sum+=b[p];
return ;
}
int mid = (T[pos].l+T[pos].r)/2;
if(p<=mid) update(pos*2,p);
if(p>mid) update(pos*2+1,p);
T[pos].sum=T[pos*2+1].sum+T[pos*2].sum;
return ;
}
inline int query(int cnt,int now){
if(T[cnt].sum<=now) return T[cnt].num;
if(T[cnt].l==T[cnt].r) {
return now/b[T[cnt].l];
}
int mid = (T[cnt].l+T[cnt].r)/2;
if(T[cnt*2].sum<=now) return T[cnt*2].num+query(cnt*2+1,now-T[cnt*2].sum);
else {
return query(cnt*2,now);
}
}
int main()
{
int q;
read(q);
while (q--) {
read(n);
read(m);
for(int i=1;i<=n;++i) {
read(a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
int cnt=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++) {
a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;//离散化
}
build(1,cnt,1);
update(1,a[1]);
ans[1]=0;
for(int i=2;i<=n;i++){
int x=query(1,m-b[a[i]]);
ans[i]=i-1-x;
update(1,a[i]);
}
for(int i=1;i<=n;i++) {
print(ans[i]);
putchar(' ');
}
putchar('\n');
}
return 0;
}
ps:每次去找前n-1项有多少个数+现在的数小于等于m。