之前刷了一点主席树的题目,但是没有系统的做过权值线段树的题目。主席树是多根权值线段树的综合。权值线段树可以解决在总区间里求第k大的问题。在普通的线段树里,我们每一个节点维护的是权值大小。但是在权值线段树里维护的是数字在数组中的相对的位置。所以权值线段树经常需要和离散化结合在一起。
昨天hdu多校一个权值线段树的题目。
Find the answer
Time Limit: 4000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 4521 Accepted Submission(s): 508
Problem Description

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 ∑ij=1Wj<=m. So what’s the minimum number of chosen elements to meet the requirements above?.
Input
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
Output
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 ∑ij=1Wj<=m.
Sample Input
2
7 15
1 2 3 4 5 6 7
5 100
80 40 40 40 60

Sample Output
0 0 0 0 0 2 3
0 1 1 2 3
给定n个数和一个数字m,对于位置i,我们需要保证从1~i-1的数字和小于m,那么我们最少使前面多少个数字为0。一开始就是暴力主席树,每次找最大的去删除。一开始数组开小了,该返回re却返回的是TLE。1e5的数据量我们最差也应该是nlogn的时间复杂度。这时权值线段树就派上用场了。
对于每一个位置的数字,我们找出在他之前小于等于m-a[i]的数字的个数,再用I-ans-1就是我们要知道的答案了。统计完答案之后,就在将这个数插入到线段树中就好了。1e9的数据量,需要离散化。
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
using namespace std;const int maxx=2e5+100;
struct node{int l;int r;ll sum;ll num;
}p[maxx<<2];
ll a[maxx],b[maxx];
int n;
ll m;
int getid(ll x,int len)
{return lower_bound(b+1,b+1+len,x)-b;
}
void pushup(int cur)
{p[cur].sum=p[cur<<1].sum+p[cur<<1|1].sum;//sum代表的是前面几个数字的和p[cur].num=p[cur<<1].num+p[cur<<1|1].num;//num代表的是数字出现的次数
}
void build(int l,int r,int cur)
{p[cur].l=l;p[cur].r=r;p[cur].sum=p[cur].num=0;if(l==r) return ;int mid=(l+r)>>1;build(l,mid,cur<<1);build(mid+1,r,cur<<1|1);pushup(cur);
}
int query(ll ant,int cur)
{if(p[cur].sum<=ant) return p[cur].num;//如果这个节点的权值和小于ant就直接返回数字个数就可以if(p[cur].l==p[cur].r) return min(ant/b[p[cur].l],p[cur].num);//如果到达了叶子节点,我们就去凑antif(ant<=p[cur<<1].sum) return query(ant,cur<<1);//如果小于左子树的权值,就进入左子树else return p[cur<<1].num+query(ant-p[cur<<1].sum,cur<<1|1);//大于左子树,就加上左子树的数字个数再加上右子树符合题意的数字个数
}
void update(int pos,ll add,int cur)
{int L=p[cur].l;int R=p[cur].r;if(L==R)//单点更新,到达叶子节点才更新{p[cur].sum+=add;p[cur].num++;return ;}int mid=(L+R)>>1;if(pos<=mid) update(pos,add,cur<<1);else update(pos,add,cur<<1|1);pushup(cur);
}
int main()
{int t;scanf("%d",&t);while(t--){scanf("%d%lld",&n,&m);for(int i=1;i<=n;i++) {scanf("%lld",&a[i]);b[i]=a[i];}sort(b+1,b+1+n);int len=unique(b+1,b+1+n)-b-1;//排序后的离散化build(1,n,1);for(int i=1;i<=n;i++){int ans=query(m-a[i],1);printf("%d ",i-ans-1);update(getid(a[i],len),a[i],1);//统计完答案后就更新}printf("\n");}return 0;
}

结束之后刷了刷权值线段树的题目。orz
普通的平衡树
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output
对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
Hint
1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]
权值线段树的题目貌似splay树都能解决。但是不会splay树。。
总共有6中操作。对于这个题目我们进行离线处理。
1.在那个数字的位置加一。
2.在那个数字的位置减一。
3.假设x在离散化后的数组中的位置是pos,我们统计出前pos-1有多少个数,再加一就好了
4.求第k大。。
5.前驱,假设x在离散化后的数组中的位置是pos,我们求出前pos-1的数目为num,去查询排名第num的数字是什么就好了。
6.后驱,假设x在离散化后的数组中的位置是pos,我们求出前pos的数目为num,去查询排名为num+1的数字是什么就好了。
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#define ll long long
using namespace std;const int maxx=1e5+100;
struct N{int id;int v;
}e[maxx];
struct node{int l;int r;int num;
}p[maxx<<2];
int a[maxx],b[maxx];
int n,m;inline void pushup(int cur)
{p[cur].num=p[cur<<1].num+p[cur<<1|1].num;
}
inline void build(int l,int r,int cur)
{p[cur].l=l;p[cur].r=r;p[cur].num=0;if(l==r) return ;int mid=l+r>>1;build(l,mid,cur<<1);build(mid+1,r,cur<<1|1);
}
inline void update(int cur,int pos,int add)
{int L=p[cur].l;int R=p[cur].r;if(L==R){p[cur].num+=add;return ;}int mid=L+R>>1;if(pos<=mid) update(cur<<1,pos,add);else update(cur<<1|1,pos,add);pushup(cur);
}
inline int query1(int l,int r,int cur)
{if(l>r) return 0;if(p[cur].l>=l&&p[cur].r<=r) return p[cur].num;int mid=p[cur].l+p[cur].r>>1;if(r<=mid) return query1(l,r,cur<<1);if(l>mid) return query1(l,r,cur<<1|1);return query1(l,mid,cur<<1)+query1(mid+1,r,cur<<1|1);
}
//int query1(int l,int r,int cur){
//  if(p[cur].l>=l&&p[cur].r<=r) return p[cur].num;
//  int mid=p[cur].l+p[cur].r>>1;
//  int ans=0;
//  if(l<=mid) ans+=query1(l,r,cur<<1);
//  if(r>mid) ans+=query1(l,r,cur<<1|1);
//    return ans;
//}
inline int query2(int pos,int cur)
{int L=p[cur].l;int R=p[cur].r;if(L==R) return L;if(pos<=p[cur<<1].num) return query2(pos,cur<<1);else return query2(pos-p[cur<<1].num,cur<<1|1);
}
int main()
{scanf("%d",&n);char op[2];int x,pos;int cnt=0;//build(1,maxx,1);for(int i=1;i<=n;i++){scanf("%d%d",&e[i].id,&e[i].v);if(e[i].id!=4) b[++cnt]=e[i].v;}sort(b+1,b+1+cnt);int len=unique(b+1,b+1+cnt)-b-1;build(1,len,1);for(int i=1;i<=n;i++){if(e[i].id==1) {pos=lower_bound(b+1,b+1+len,e[i].v)-b;update(1,pos,1);}else if(e[i].id==2) {pos=lower_bound(b+1,b+1+len,e[i].v)-b;update(1,pos,-1);}else if(e[i].id==3){pos=lower_bound(b+1,b+1+len,e[i].v)-b;printf("%d\n",query1(1,pos-1,1)+1);}else if(e[i].id==4) {printf("%d\n",b[query2(e[i].v,1)]);}else if(e[i].id==5){pos=lower_bound(b+1,b+1+len,e[i].v)-b;printf("%d\n",b[query2(query1(1,pos-1,1),1)]);}else if(e[i].id==6){pos=lower_bound(b+1,b+1+len,e[i].v)-b;printf("%d\n",b[query2(query1(1,pos,1)+1,1)]);}}return 0;
}
/*13
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
3 81968
3 492737
3 106465*/

郁闷的出纳员
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的
工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好
,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我
真不知道除了调工资他还做什么其它事情。工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位
员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员
工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘
了一位新员工,我就得为他新建一个工资档案。老板经常到我这边来询问工资情况,他并不问具体某位员工的工资
情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后
告诉他答案。好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样
,不是很困难吧?
Input
第一行有两个非负整数n和min。n表示下面有多少条命令,min表示工资下界。
接下来的n行,每行表示一条命令。命令可以是以下四种之一:
名称 格式 作用
I命令 I_k 新建一个工资档案,初始工资为k。
如果某员工的初始工资低于工资下界,他将立刻离开公司。
A命令 A_k 把每位员工的工资加上k
S命令 S_k 把每位员工的工资扣除k
F命令 F_k 查询第k多的工资
_(下划线)表示一个空格,I命令、A命令、S命令中的k是一个非负整数,F命令中的k是一个正整数。
在初始时,可以认为公司里一个员工也没有。
I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000
Output
输出行数为F命令的条数加一。
对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,
如果k大于目前员工的数目,则输出-1。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
Sample Input
9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
Sample Output
10
20
-1
2
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
using namespace std;const int base=200020;
const int maxx=400040;
int add,sum;
int n,m;struct node{int l;int r;int num;int lazy;
}p[maxx<<2];void pushup(int cur)
{p[cur].num=p[cur<<1].num+p[cur<<1|1].num;
}
void pushdown(int cur)
{if(p[cur].lazy){p[cur<<1].num=p[cur<<1|1].num=0;p[cur<<1].lazy=p[cur<<1|1].lazy=1;p[cur].lazy=0;}
}
void build(int l,int r,int cur)
{p[cur].l=l;p[cur].r=r;p[cur].num=p[cur].lazy=0;if(l==r) return ;int mid=l+r>>1;build(l,mid,cur<<1);build(mid+1,r,cur<<1|1);
}
void insert(int cur,int add)
{int L=p[cur].l;int R=p[cur].r;if(L==R) {p[cur].num++;return ;}pushdown(cur);int mid=L+R>>1;if(add<=mid) insert(cur<<1,add);else insert(cur<<1|1,add);pushup(cur);
}
void update(int cur,int add)
{int L=p[cur].l;int R=p[cur].r;if(L==R){if(p[cur].l<add) p[cur].num=0;return ;}if(p[cur].r<add){p[cur].num=0;p[cur].lazy=1;return ;}pushdown(cur);int mid=(L+R)>>1;if(add<=mid) update(cur<<1,add);else update(cur<<1,add),update(cur<<1|1,add);pushup(cur);
}
int query(int cur,int k)
{if(p[cur].l==p[cur].r) return p[cur].l;int L=p[cur].l;int R=p[cur].r;pushdown(cur);if(k<=p[cur<<1].num) return query(cur<<1,k);else return query(cur<<1|1,k-p[cur<<1].num);pushup(cur);
}
int main()
{scanf("%d%d",&n,&m);build(1,maxx,1);char op[2];int x;sum=add=0;while(n--){scanf("%s%d",op,&x);if(op[0]=='I'){if(x<m) continue;sum++;insert(1,x-add+base);}else if(op[0]=='A') add+=x;else if(op[0]=='S'){add-=x;update(1,m-add+base);}else if(op[0]=='F'){if(p[1].num<x) puts("-1");else printf("%d\n",query(1,p[1].num-x+1)+add-base);}}printf("%d\n",sum-p[1].num);
}

路漫漫其修远兮,努力加油a啊(o)/~

权值线段树小结(hdu多校,普通平衡树,郁闷的出纳员)相关推荐

  1. 2019CCPC网络赛 1002 HDU 6703(权值线段树)

    2019CCPC网络赛 1002 HDU 6703(权值线段树) 思路:用权值线段树存题目给的数据后,2操作就是求权值线段树中大于等于k的部分中,靠近左端点的第一个大于r的值(这个求出来的只是原序列中 ...

  2. Minimum Inversion Number HDU - 1394(权值线段树/树状数组)

    The inversion number of a given number sequence a1, a2, -, an is the number of pairs (ai, aj) that s ...

  3. 【HDU - 5489】Removed Interval(离散化,权值线段树,思维,最长上升子序列)

    题干: Given a sequence of numbers A=a1,a2,-,aNA=a1,a2,-,aN, a subsequence b1,b2,-,bkb1,b2,-,bk of AA i ...

  4. 2019牛客多校第七场E Find the median 权值线段树+离散化

    Find the median 题目链接: https://ac.nowcoder.com/acm/contest/887/E 题目描述 Let median of some array be the ...

  5. 【bzoj2770】YY的Treap 权值线段树

    题目描述 志向远大的YY小朋友在学完快速排序之后决定学习平衡树,左思右想再加上SY的教唆,YY决定学习Treap.友爱教教父SY如砍瓜切菜般教会了YY小朋友Treap(一种平衡树,通过对每个节点随机分 ...

  6. 树套树 ----- P1975 [国家集训队]排队(树状数组套权值线段树求动态逆序对)

    解题思路: 首先我们知道交换两个数a[l]和a[r]a[l]和a[r]a[l]和a[r]影响到的区间是[l+1,r−1][l+1,r-1][l+1,r−1] 对于a[l]a[l]a[l],我们要减去[ ...

  7. 树套树 ---- 树状数组套权值线段树模板题 P2617 Dynamic Rankings 动态第K大

    题目链接 题目大意: 给你一个数组aaa,aaa有两个操作 询问aaa中[l,r][l,r][l,r]区间里面第kkk小的数是哪个? 修改axa_xax​为yyy 解题思路: 首先我们知道权值线段树是 ...

  8. codevs1688 求逆序对(权值线段树)

    1688 求逆序对  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果 题目描述 Description 给定一个序列a1,a2,-,an,如 ...

  9. 【HDU6701】Make Rounddog Happy【权值线段树+双向单调队列】

    题意:给你一个序列,求满足要求的子序列个数,其中要求为: 1.子序列的max-子序列长度len<=k 2.子序列中不出现重复的数字 题解:首先看到子序列max,很容易想到枚举最大值然后分治,这个 ...

最新文章

  1. 祝「杭州程序媛」母亲节快乐!
  2. 【C 语言】字符串操作 ( C 字符串 | 字符数组始化 )
  3. php excel cpu高,phpexcel读文件的时候cpu99%
  4. 在Swing和Swt中使用JavaFX
  5. java代码_Java 代码实现排序算法
  6. Codeforces Round #327 div2
  7. Netflix推出Hollow,处理内存数据集的Java库
  8. 图像处理——SIFT算法
  9. 用Delphi编写的一个上网助手
  10. linux启动盘无法格式化u盘启动盘,U盘做了启动盘后无法格式化怎么办实测解决...
  11. 普通糖尿病人1周食谱
  12. android程序图标透明,怎么把android手机软件图标变透明
  13. win7无法连接打印机拒绝访问_打印机拒绝访问,小编教你打印机拒绝访问无法连接怎么解决...
  14. 微信小程序里面嵌套的h5使用微信sdk配置踩坑
  15. TortoiseSVN使用简介
  16. 【FACT】函数使用技巧
  17. 【操作系统-Windows】组件服务 DCOM 配置编辑安全属性
  18. 003_创建simulink文件
  19. 浅尝 | 从 0 到 1 Vue 组件库封装
  20. TSCA你了解多少?什么产品需要做TSCA认证

热门文章

  1. IOS15自定义UICollectionView的使用
  2. 华三交换机mode是什么意思_POE交换机150米、长距离250米传输是什么意思?
  3. 台式电脑键盘按键错乱_电脑键盘各个按键有哪些作用 电脑键盘各个按键作用介绍...
  4. ldap seach java_从LDAP(Java)检索信息
  5. mysql数据库发布到web服务器上_web应用发布至服务器
  6. 外星人台式电脑_执掌游戏世界 外星人ALIENWARE m15丨17.9mm机身轻悍降临【电脑组装】公众号分享台式机笔记本电脑 京东商城特惠通道...
  7. Flutter开发之实现沉浸式状态栏的效果
  8. Android开发Android studio之gradle打包apk重命名的方法
  9. UIview需要知道的一些事情:setNeedsDisplay、setNeedsLayout
  10. 想靠大数据创业 你需要了解什么