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

2024-02-01 15:32

本文主要是介绍权值线段树小结(hdu多校,普通平衡树,郁闷的出纳员),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前刷了一点主席树的题目,但是没有系统的做过权值线段树的题目。主席树是多根权值线段树的综合。权值线段树可以解决在总区间里求第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多校,普通平衡树,郁闷的出纳员)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/667674

相关文章

Java中Object类的常用方法小结

《Java中Object类的常用方法小结》JavaObject类是所有类的父类,位于java.lang包中,本文为大家整理了一些Object类的常用方法,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. public boolean equals(Object obj)2. public int ha

redis-cli命令行工具的使用小结

《redis-cli命令行工具的使用小结》redis-cli是Redis的命令行客户端,支持多种参数用于连接、操作和管理Redis数据库,本文给大家介绍redis-cli命令行工具的使用小结,感兴趣的... 目录基本连接参数基本连接方式连接远程服务器带密码连接操作与格式参数-r参数重复执行命令-i参数指定命

Python视频处理库VidGear使用小结

《Python视频处理库VidGear使用小结》VidGear是一个高性能的Python视频处理库,本文主要介绍了Python视频处理库VidGear使用小结,文中通过示例代码介绍的非常详细,对大家的... 目录一、VidGear的安装二、VidGear的主要功能三、VidGear的使用示例四、VidGea

Python中json文件和jsonl文件的区别小结

《Python中json文件和jsonl文件的区别小结》本文主要介绍了JSON和JSONL两种文件格式的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下... 众所周知,jsON 文件是使用php JSON(JavaScripythonpt Object No

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

python安装完成后可以进行的后续步骤和注意事项小结

《python安装完成后可以进行的后续步骤和注意事项小结》本文详细介绍了安装Python3后的后续步骤,包括验证安装、配置环境、安装包、创建和运行脚本,以及使用虚拟环境,还强调了注意事项,如系统更新、... 目录验证安装配置环境(可选)安装python包创建和运行Python脚本虚拟环境(可选)注意事项安装

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

Redis的Hash类型及相关命令小结

《Redis的Hash类型及相关命令小结》edisHash是一种数据结构,用于存储字段和值的映射关系,本文就来介绍一下Redis的Hash类型及相关命令小结,具有一定的参考价值,感兴趣的可以了解一下... 目录HSETHGETHEXISTSHDELHKEYSHVALSHGETALLHMGETHLENHSET

python中cv2.imdecode()与cv2.imencode()的使用小结

《python中cv2.imdecode()与cv2.imencode()的使用小结》本文介绍了cv2.imencode()和cv2.imdecode()函数的使用,文中通过示例代码介绍的非常详细,对... 目录1、图片路径带中文的读取和写入1.1 读取1.2 写入2、在网络中传输图片cv2.imencod