可持久化线段树的学习(区间第k大和查询历史版本的数据)(杭电多校赛第二场1011)
以前我们学习了线段树可以知道,线段树的每一个节点都储存的是一段区间,所以线段树可以做简单的区间查询,更改等简单的操作。
而后面再做有些题目,就可能会碰到一种回退的操作。这里的回退是指回到未做各种操作之前的状态。
回退的时候,如果暴力点,就直接将每步所操作的线段树都存下来,然后直接翻阅回去,这种方法虽然简单,但是对空间和时间的需求太大了,肯定不能过。
所以这时候我们就可以选择可持久化操作。
可持久化是数据结构里面的一种方法,其总体就是把一个数据结构的历史状态全部都保存下来,从而能够快速的查找之前出现过的某个操作的结果,并且还可以对其继续操作。但是与直接暴力不一样,做修改的时候,每一次修改,只有一条链上的节点被修改,而其他的节点信息都没有变。因此,我们就可以只对这一次的修改操作新建包括一个新根在内的logn个节点,其他的节点我们与上一课树共用。这样一来,我们既能保存之前的信息,又能进行修改操作。
而一般常用的数据结构里面用到的可持久化的线段树。
可持久化的线段树,又叫主席树。其思想大概就如图。在每一步的线段树操作后将改变的那一条链给单独拎出来,然后新建一次链,而没有改变的链就不动,再将新链与不动的那部分重新组合一下,就成了。

直接看模板裸题吧 https://www.luogu.org/problemnew/show/P3919
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int inf = 0x3f3f3f3f;
int dir[][]={{,},{,},{,},{,-},{-,},{-,-},{,-},{-,}};
#define pi acos(-1)
#define ls rt<<1
#define rs rt<<1|1
#define me0(s) memset(s,0,sizeof(s))
#define me1(s) memset(s,1,sizeof(s))
#define mef(s) memset(s,-1,sizeof(s))
#define meinf(s) memset(s,inf,sizeof(s))
const int N=1e5+;
struct node{
int lc,rc;
ll v;
}tr[N<<];
int root[N<<]; // root[i]表示版本号为i的线段树的根节点编号
ll a[N];
int n,m,tot;
int build(int l,int r){
int pos=++tot;
if(l==r){
tr[pos].v=a[l];
}
else{
int m=(l+r)>>;
tr[pos].lc=build(l,m);
tr[pos].rc=build(m+,r);
}
return pos;
}
int query(int pos,int p,int l,int r){ //查询版本pos中a[p]的值
if(l==r){
return tr[pos].v;
}
int m=(l+r)>>;
if(p<=m) return query(tr[pos].lc,p,l,m);
else return query(tr[pos].rc,p,m+,r);
}
int update(int old,int p,int c,int l,int r){
//在old版本上修改a[p]为c
int pos=++tot;
if(l==r){
tr[pos].v=c;
return pos;
}
tr[pos].lc=tr[old].lc;
tr[pos].rc=tr[old].rc; //复制以前的树
int m=(l+r)>>;
if(p<=m) tr[pos].lc=update(tr[old].lc,p,c,l,m);
else tr[pos].rc=update(tr[old].rc,p,c,m+,r);
return pos;
}
int main(int argc, char * argv[])
{
std::ios::sync_with_stdio(false);
while(cin>>n>>m){
tot=;
for(int i=;i<=n;i++) cin>>a[i];
root[]=build(,n);
int v,x,l,w;
for(int i=;i<=m;i++){
cin>>v>>x>>l;
if(x==){
cin>>w;
root[i]=update(root[v],l,w,,n);
}
else{
root[i]=root[v];
cout<<query(root[v],l,,n)<<endl;
}
}
}
return ;
}
然后是用主席树求区间第k大/小的板子题目
https://www.luogu.org/problemnew/show/P3834
//区间第k小
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
const int maxn=1e5+;
ll cnt,n,m,x,y,k,a[maxn];
int root[maxn];
struct node{
int l,r,sum;
}tr[maxn*];
vector<int> v;
void init(){
cnt=;
tr[cnt].l=;
tr[cnt].r=;
tr[cnt].sum=;
root[cnt]=;
v.clear();
}
ll getid(ll x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+;
}
void update(int l,int r,int &x,int y,int pos){
tr[++cnt]=tr[y];
tr[cnt].sum++;
x=cnt;
if(l==r) return ;
int m=(l+r)>>;
if(pos<=m) update(l,m,tr[x].l,tr[y].l,pos);
else update(m+,r,tr[x].r,tr[y].r,pos);
}
ll query(ll l,ll r,ll x,ll y,ll k){
if(l==r) return l;
ll m=(l+r)>>;
ll sum=tr[tr[y].l].sum-tr[tr[x].l].sum;
if(sum>=k) return query(l,m,tr[x].l,tr[y].l,k);
else return query(m+,r,tr[x].r,tr[y].r,k-sum);
}
int main(){
cin>>n>>m;
init();
for(int i=;i<=n;i++){
scanf("%lld",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=;i<=n;i++)
update(,n,root[i],root[i-],getid(a[i]));
while(m--){
int l,r,k;
cin>>l>>r>>k;
cout<<v[query(,n,root[l-],root[r],k)-]<<endl;
}
return ;
}
还有一题杭电多校赛第二场的:http://acm.hdu.edu.cn/showproblem.php?pid=6601
给定数组a,求所给定的区间的内能构成的最大的三角形的周长。
首先想到的是数组内排序,然后一个一个遍历,那么复杂度就达到了O(mnlogn),肯定超时了。
然后赛后补题,用主席树求出区间的第k小的值,然后代替排序。
//区间第k小
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
const int maxn=1e5+;
ll cnt,n,m,x,y,k,a[maxn];
int root[maxn];
struct node{
int l,r,sum;
}tr[maxn*];
vector<int> v;
void init(){
cnt=;
tr[cnt].l=;
tr[cnt].r=;
tr[cnt].sum=;
root[cnt]=;
v.clear();
}
ll getid(ll x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+;
}
void update(int l,int r,int &x,int y,int pos){
tr[++cnt]=tr[y];
tr[cnt].sum++;
x=cnt;
if(l==r) return ;
int m=(l+r)>>;
if(pos<=m) update(l,m,tr[x].l,tr[y].l,pos);
else update(m+,r,tr[x].r,tr[y].r,pos);
}
ll query(ll l,ll r,ll x,ll y,ll k){
if(l==r) return l;
ll m=(l+r)>>;
ll sum=tr[tr[y].l].sum-tr[tr[x].l].sum;
if(sum>=k) return query(l,m,tr[x].l,tr[y].l,k);
else return query(m+,r,tr[x].r,tr[y].r,k-sum);
}
int main(){
while(~scanf("%lld%lld",&n,&m)){
init();
for(int i=;i<=n;i++){
scanf("%lld",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=;i<=n;i++)
update(,n,root[i],root[i-],getid(a[i]));
while(m--){
int l,r;
scanf("%d%d",&l,&r);
ll l1=,l2=,l3=;
ll ans=-;
if(r-l+<)printf("-1\n");
else{
for(int k=(r-l+);k>;k--){
int x=v[query(,n,root[l-],root[r],k)-];
if(l1==) l1=x;
else if(l2==) l2=x;
else if(l3==) l3=x;
else{
l1=l2;
l2=l3;
l3=x;
}
if(l3+l2>l1) {
ans=1ll*l1+l2+l3;
break;
}
}
printf("%lld\n",ans);
}
}
}
return ;
}
可持久化线段树的学习(区间第k大和查询历史版本的数据)(杭电多校赛第二场1011)的更多相关文章
- HDU 2665.Kth number-可持久化线段树(无修改区间第K小)模板 (POJ 2104.K-th Number 、洛谷 P3834 【模板】可持久化线段树 1(主席树)只是输入格式不一样,其他几乎都一样的)
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 主席树(可持久化线段树)静态区间第K小
传送门主席树 #include <bits/stdc++.h> #define int long long using namespace std; const int maxn=2e5+ ...
- hdu2665可持久化线段树,求区间第K大
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)
题目链接 传送门 题意 给你一棵无根树,要你寻找一个根节点使得在将一条边权变为\(0\)后,离树根最远的点到根节点的距离最小. 思路 本题和求树的直径很像,不过要记得的东西有点多,且状态也很多. \( ...
- HDU 4417.Super Mario-可持久化线段树(无修改区间小于等于H的数的个数)
Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 2019杭电多校第三场hdu6609 Find the answer(线段树)
Find the answer 题目传送门 解题思路 要想变0的个数最少,显然是优先把大的变成0.所以离散化,建立一颗权值线段树,维护区间和与区间元素数量,假设至少减去k才能满足条件,查询大于等于k的 ...
- [2019杭电多校第三场][hdu6609]Find the answer(线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609 大致题意是求出每个位置i最小需要将几个位置j变为0(j<i),使得$\sum_{j=1}^ ...
- 2019杭电多校第六场hdu6638 Snowy Smile(线段树+枚举)
Snowy Smile 题目传送门 解题思路 先把y离散化,然后把点按照x的大小进行排序,我们枚举每一种x作为上边界,然后再枚举其对应的每一种下边界.按照这种顺序插入点,这是一个压维的操作,即在线段树 ...
- 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)
Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...
随机推荐
- 在centos 6.9 x64下安装code::blocks步骤
1.yum groupinstall "Development tools" 2.yum install gtk2* 3.安装wxWidgets 下载地址:https://www. ...
- 第九篇:Spring的applicationContext.xml配置总结
在前面的一篇日志中,记录了web.xml配置启动的顺序,web启动到监听器ContextLoaderListener时,开始加载spring的配置文件applicationContext.xml(通常 ...
- sql server2014显示sa无法登录的错误
博主用的是sql serser2014,不过这个问题的方法也适用于2012等其他版本. 当用sa登录的时候,提示如下错误: A connection was successfully establis ...
- [NOIP2019模拟赛]数数(gcd)
题目大意: 求l~r中有多少数与x互质,带单点修改 分析: 两个30的部分分很好打: ·n<=1000暴力O(nq)就好了 ·$a_i<=100$用树状数组维护每个x的前缀和就好了 100 ...
- hdu5952 Counting Cliques 技巧dfs
题意:一个有N个点M条边的图,球其中由S个点构成的团的个数.一个团是一个完全子图. 没有什么好办法,只有暴力深搜,但是这里有一个神奇的操作:将无向图转为有向图:当两个点编号u<v时才有边u-&g ...
- 一.ES6的开发环境搭建
前言: 现在的Chrome浏览器已经支持ES6了,但是有些低版本的浏览器还是不支持ES6的语法,这就需要我们把ES6的语法自动的转变成ES5的语法.Webpack是有自动编译转换能力的,除了Webpa ...
- thinkphp 虚拟模型
虚拟模型是指虽然是模型类,但并不会真正的操作数据库的模型.有些时候,我们建立模型类但又不需要进行数据库操作,仅仅是借助模型类来封装一些业务逻辑,那么可以借助虚拟模型来完成.虚拟模型不会自动连接数据库, ...
- 第七章 Odoo 12开发之记录集 - 使用模型数据
在上一篇文章中,我们概览了模型创建以及如何从模型中载入和导出数据.现在我们已有数据模型和相关数据,是时候学习如何编程与其进行交互 了.模型的 ORM(Object-Relational Mapping ...
- vue如何关闭sourceMap
Vue打包后出现一些map文件的解决办法: 问题: 可能很多人在做vue项目打包,打包之后js中,会自动生成一些map文件,那我们怎么把它去掉不要呢? 1,运行 cnpm run build 开始 ...
- python随机数(转载)
随机生成 0 到 1 之间的浮点数 random.random() 方法会返回 [0.0, 1.0) 之间的浮点数,注意,这是一个左闭右开的区间,随机数可能会是 0 但不可能为 1 . 随机生成 a ...