线段树离散化

线段树离散化

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

 

题目:

题目描述
Given a sequence of n integers called W and an integer m. For each i (1 <= i <= n), you can choose some elements Wk (1 <= k < i), and change them to zero to make  So what’s the minimum number of chosen elements to meet the requirements above?.

 

输入
The first line contains an integer Q — the number of test cases. 
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

 

输出
For each test case, you should output n integers in one line: i-th integer means the minimum number of chosen elements Wk (1 <= k < i), and change them to zero to make 

 

样例输入

复制样例数据

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值