既然选择了远方,便只顾风雨兼行|

H_W_Y

园龄:1年11个月粉丝:28关注:15

2023-10-25 09:56阅读: 26评论: 0推荐: 0

平衡树

20231025

平衡树

前期的内容写在PPT上面。。。

平衡树线段树五问:

  1. 每个节点需要记录那些信息?
  2. 需要哪些标记?
  3. 如何下传标记?
  4. 如何区间整体修改?
  5. 如何合并区间(上传信息)?

image

P3215 [HNOI2011] 括号修复 / [JSOI2011]括号序列

Statement

现在给你一个长度为 n 的由()组成的字符串,位置标号从 1n。对这个字符串有下列四种操作:

  • Replace a b c:将 [a,b] 之间的所有括号改成 c。假设原来的字符串为:))())())(,那么执行操作 Replace 2 7 ( 后原来的字符串变为:)(((((()(

  • Swap a b:将 [a,b] 之间的字符串翻转。假设原来的字符串为:))())())(,那么执行操作 Swap 3 5 后原来的字符串变为:))))(())(

  • Invert a b:将 [a,b] 之间的 ( 变成 )) 变成 (。假设原来的字符串为:))())())(,那么执行操作 Invert 4 8 后原来的字符串变为:))((()(((

  • Query a b:询问 [a,b] 之间的字符串至少要改变多少位才能变成合法的括号序列。改变某位是指将该位的 ( 变成 )) 变成 (。注意执行操作 Query 并不改变当前的括号序列。假设原来的字符串为:))())())(,那么执行操作 Query 3 6 的结果为 2,因为要将位置 5)变成(并将位置 6(变成)

对于 100% 的数据,1n,q105

Solution

问题主要在于 query 时的查询,

我们钦定 ( 记作 -1) 记作 1 ,于是我们只需要维护前缀最大值 prmx 和后缀最小值 sfmn ,那么一个区间的答案就是:

prmx/2+sfmx/2

画画图就很容易证明。

于是现在考虑具体操作:

  1. 每个节点需要记录那些信息:区间和,前缀最大最小,后缀最大最小
  2. 需要哪些标记:翻转标记,覆盖标记,取反标记
  3. 如何下传标记:翻转(直接像文艺平衡树),覆盖(把区间维护的信息都改变),取反(前后缀最大最小交换,prmxprmn,prmnprmax
  4. 区间整体修改?把前驱后继分别转到根和根的右儿子。
  5. 合并区间?枚举是否有左右儿子即可。

为什么有那么长的代码???

注意前面有一个哨兵,所以转的时候是转 l,r+2

#include <bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m,cnt=0,rt=0,a[N];
struct Splay{
  int fa,siz,sum,val,prmx,prmn,sfmx,sfmn,s[2],inv,rev,cov;
}tr[N];

#define lc(p) tr[p].s[0]
#define rc(p) tr[p].s[1]

void pu(int p){
  tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1;
  tr[p].sum=tr[lc(p)].sum+tr[rc(p)].sum+tr[p].val;
  tr[p].prmx=max(tr[lc(p)].prmx,tr[rc(p)].prmx+tr[lc(p)].sum+tr[p].val);
  tr[p].prmn=min(tr[lc(p)].prmn,tr[rc(p)].prmn+tr[lc(p)].sum+tr[p].val);
  tr[p].sfmx=max(tr[rc(p)].sfmx,tr[rc(p)].sum+tr[p].val+tr[lc(p)].sfmx);
  tr[p].sfmn=min(tr[rc(p)].sfmn,tr[rc(p)].sum+tr[p].val+tr[lc(p)].sfmn);
} 

void cov(int p,int val){
  if(!p) return;
  tr[p].cov=val;
  if(val==1){
  	tr[p].sum=tr[p].siz;
  	tr[p].prmn=tr[p].sfmn=0;	
	tr[p].prmx=tr[p].sfmx=tr[p].sum;
  }
  else{
  	tr[p].sum=~tr[p].siz+1;
  	tr[p].prmx=tr[p].sfmx=0;
  	tr[p].prmn=tr[p].sfmn=tr[p].sum;
  }
  tr[p].val=val;
  tr[p].inv=0;
}

void rev(int p){
  if(!p) return;
  tr[p].rev^=1;
  swap(lc(p),rc(p));
  swap(tr[p].prmx,tr[p].sfmx);
  swap(tr[p].prmn,tr[p].sfmn);
}

void inv(int p){
  if(!p) return;
  if(tr[p].cov!=0){
  	if(tr[p].cov==1) cov(p,-1);
  	else cov(p,1);
  	return;
  }
  tr[p].inv^=1;
  int t=tr[p].prmx;
  tr[p].prmx=~tr[p].prmn+1;
  tr[p].prmn=~t+1;
  t=tr[p].sfmx;
  tr[p].sfmx=~tr[p].sfmn+1;
  tr[p].sfmn=~t+1;
  tr[p].sum=~tr[p].sum+1;
  tr[p].val=~tr[p].val+1;
}

void pd(int p){
  if(tr[p].cov!=0){
  	cov(lc(p),tr[p].cov);
  	cov(rc(p),tr[p].cov);
  	tr[p].cov=0;
  }
  if(tr[p].inv!=0) inv(lc(p)),inv(rc(p)),tr[p].inv=0;
  if(tr[p].rev!=0) rev(lc(p)),rev(rc(p)),tr[p].rev=0;
}

void rotate(int x){
  int y=tr[x].fa,z=tr[y].fa;
  int k=(rc(y)==x);
  pd(y);pd(x);
  tr[y].s[k]=tr[x].s[k^1];
  tr[tr[x].s[k^1]].fa=y;
  tr[x].s[k^1]=y;
  tr[y].fa=x;
  tr[z].s[(rc(z)==y)]=x;
  tr[x].fa=z;
  pu(y);pu(x); 
}

void splay(int x,int k){
  while(tr[x].fa!=k){
  	int y=tr[x].fa,z=tr[y].fa;
  	if(z!=k) ((rc(y)==x)^(rc(z)==y))?rotate(x):rotate(y);
  	rotate(x);
  }
  if(k==0) rt=x;
}

int find(int k){
  int p=rt;
  while(k){
  	pd(p);
  	if(k<=tr[lc(p)].siz) p=lc(p);
  	else if(k>tr[lc(p)].siz+1) k-=tr[lc(p)].siz+1,p=rc(p);
  	else break;
  }
  splay(p,0);
  return p;
}

void invert(int l,int r){
  int x=find(l),y=find(r+2);
  splay(x,0);splay(y,x);
  inv(lc(y));
  pu(y);pu(x);
}

void reverse(int l,int r){
  int x=find(l),y=find(r+2);
  splay(x,0);splay(y,x);
  rev(lc(y));
  pu(y);pu(x);
}

void replace(int l,int r,int val){
  int x=find(l),y=find(r+2);
  splay(x,0);splay(y,x);
  cov(lc(y),val);
  pu(y);pu(x);
}

int query(int l,int r){
  int x=find(l),y=find(r+2);
  splay(x,0);splay(y,x);
  x=lc(y);
  return ((tr[x].prmx+1)/2-(tr[x].sfmn-1)/2);
}

#define mid ((l+r)>>1)

int build(int l,int r,int fa){
  if(l>r) return 0;
  int p=++cnt;
  tr[p].val=tr[p].sum=a[mid];
  tr[p].siz=1;tr[p].fa=fa;
  tr[p].s[0]=build(l,mid-1,p);
  tr[p].s[1]=build(mid+1,r,p);
  pu(p);return p; 
} 

void dfs(int p){
  if(!p) return;
  pd(p);
  dfs(lc(p));
  printf("%d:%d %d->%d %d\n",p,lc(p),rc(p),tr[p].val,tr[p].siz);
  printf("%d %d %d %d %d\n",tr[p].sum,tr[p].prmx,tr[p].prmn,tr[p].sfmx,tr[p].sfmn);
  dfs(rc(p));
}

int main(){
  /*2023.10.23 H_W_Y P3215 [HNOI2011] 括号修复 / [JSOI2011]括号序列 Splay*/
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++){
  	char s=getchar();
  	while(s!='('&&s!=')') s=getchar();
  	if(s=='(') a[i]=-1;
  	else a[i]=1;
  }
  rt=build(0,n+1,0);
  for(int i=1;i<=m;i++){
  	char s[15];int l,r;
    scanf("%s",s);
    scanf("%d%d",&l,&r);
    if(s[0]=='R'){
      char ch;ch=getchar();
      while(ch!='('&&ch!=')') ch=getchar();
      replace(l,r,(ch=='(')?-1:1);
	}
	if(s[0]=='I') invert(l,r);
	if(s[0]=='S') reverse(l,r);
	if(s[0]=='Q') printf("%d\n",query(l,r));
  }
  return 0;
}

P4036 [JSOI2008] 火星人

Statement

序号 1 2 3 4 5 6 7 8 9 10 11 
字符 m a d a m i m a d a m

现在,火星人定义了一个函数 LCQ(x,y),表示:该字符串中第 x 个字符开始的字串,与该字符串中第 y 个字符开始的字串,两个字串的公共前缀的长度。比方说,LCQ(1,7)=5,LCQ(2,10)=1,LCQ(4,7)=0

在求取 LCQ 函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。

  1. 所有字符串自始至终都只有小写字母构成。
  2. M150,000
  3. 字符串长度L自始至终都满足L100,000
  4. 询问操作的个数不超过 10,000 个。

Solution

直接平衡树维护哈希值,查询时二分即可。

注意数据范围!!!

#include <bits/stdc++.h>
using namespace std;
#define ll unsigned long long
mt19937 rd(time(0));

const int N=5e5+5;
int n,l,r,x,y,idx=0,rt=0,z,pos;
const ll base=27;
ll hs[N];
char s[N];
struct Treap{
  int val,key,siz,s[2];
  ll hs;
}tr[N];

void init(){
  hs[0]=1ll;
  for(int i=1;i<=100000;i++) hs[i]=hs[i-1]*base;
}

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

void print(int x){
  int p[15],tmp=0;
  if(x==0) putchar('0');
  if(x<0) putchar('-'),x=-x;
  while(x){
  	p[++tmp]=x%10;
  	x/=10;
  }
  for(int i=tmp;i>=1;i--) putchar(p[i]+'0');
  putchar('\n');
}

#define lc(p) tr[p].s[0]
#define rc(p) tr[p].s[1]

int nwnode(int v){
  idx++;
  tr[idx].val=tr[idx].hs=v;
  tr[idx].key=rd();
  tr[idx].siz=1;
  tr[idx].s[0]=tr[idx].s[1]=0;
  return idx;
}

void pu(int p){
  tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1;
  tr[p].hs=tr[lc(p)].hs+1ll*tr[p].val*hs[tr[lc(p)].siz]+tr[rc(p)].hs*hs[tr[lc(p)].siz+1];
}

void split(int p,int k,int &x,int &y){
  if(!p){x=y=0;return;}
  if(tr[lc(p)].siz<k) x=p,split(rc(p),k-tr[lc(p)].siz-1,rc(x),y);
  else y=p,split(lc(p),k,x,lc(p));
  pu(p);
}

int merge(int x,int y){
  if(!x||!y) return (x|y);
  if(tr[x].key<tr[y].key){
  	tr[x].s[1]=merge(tr[x].s[1],y);
  	pu(x);return x;
  }
  else{
  	tr[y].s[0]=merge(x,tr[y].s[0]);
  	pu(y);return y;
  }
}

void ins(int k,int val){
  split(rt,k,x,y);
  rt=merge(merge(x,nwnode(val)),y); 
}

void del(int k){
  split(rt,k,x,z);
  split(x,k-1,x,y);
  y=merge(lc(y),rc(y));
  rt=merge(merge(x,y),z);
}

ll query(int l,int r){
  split(rt,r,x,z);
  split(x,l-1,x,y);
  ll res=tr[y].hs;
  rt=merge(merge(x,y),z);
  return res;
}

int main(){
  /*2023.10.24 H_W_Y P4036 [JSOI2008] 火星人 FHQ-Treap*/
  scanf("%s",s);init();
  for(int i=0;i<(int)strlen(s);i++) ins(i,s[i]-'a');
  n=read();
  for(int i=1;i<=n;i++){
  	scanf("%s",s);
  	if(s[0]=='Q'){
  	  int L=read(),R=read();
	  l=0,r=tr[rt].siz-max(L,R)+1;
	  while(l<r){
	  	int mid=(l+r+1)/2;
	  	if(query(L,L+mid-1)==query(R,R+mid-1)) l=mid;
	  	else r=mid-1;
	  } 
	  print(l);
    }
    else if(s[0]=='R'){
      pos=read();char ch=getchar();
      del(pos);ins(pos-1,ch-'a');
	}
	else{
	  pos=read();char ch=getchar();
	  ins(pos,ch-'a');
	}
  }
  return 0;
}

P4200 千山鸟飞绝

最后一道题哩,写起来怪激动的。

Statement

给定二维平面上的 n 个带权点,每一时刻改变一个点的坐标,最后问对于每一个点,在每一时刻内与其坐标相同的其他点的个数的最大值 a权值最大值的最大值 b

1n30000,0m300000

Solution

首先需要将坐标离散化,再用多棵平衡树维护。

发现删除一个点的答案一定不会更有,而在加入一个点的时候,

我们首先可以先把该坐标上的点打上标记,再对当前点的答案更新,

直接加入即可。

具体而言,对于每一个节点我们维护 mx,siz,tag1,tag2

每次加入一个节点时,我们用 mx 来更新新加入节点的答案,

而本来的平衡树中 tag1=max(tag1,val),tag2=max(tag2,siz) 即可。

居然一过了!!!

#include <bits/stdc++.h>
using namespace std;
#define ll long long
mt19937 rd(time(0));

const int N=3e5+5;
const ll inf=2e9;
int n,m,rt[N],idx=0,w[N],x,y,z;
ll pos[N],ans1[N],ans2[N];
struct node{
  ll w,x,y,pos;
}a[N],b[N];
struct Treap{
  int siz,id,s[2],key;
  ll val,tag1,tag2,mx,se;
}tr[N];
queue<int> q;

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

void print(ll x){
  int p[25],tmp=0;
  if(x==0) putchar('0');
  if(x<0) putchar('-'),x=-x;
  while(x){
  	p[++tmp]=x%10;
  	x/=10;
  }
  for(int i=tmp;i>=1;i--) putchar(p[i]+'0');
  putchar('\n');
}

#define lc(p) tr[p].s[0]
#define rc(p) tr[p].s[1]

void pu(int p){
  tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1;
  tr[p].mx=max(tr[p].val,max(tr[lc(p)].mx,tr[rc(p)].mx));
  if(tr[p].mx==tr[p].val) tr[p].se=max(tr[lc(p)].mx,tr[rc(p)].mx);
  else if(tr[p].mx==tr[lc(p)].mx) tr[p].se=max(max(tr[p].val,tr[lc(p)].se),tr[rc(p)].mx);
  else tr[p].se=max(max(tr[p].val,tr[rc(p)].se),tr[lc(p)].mx);  
}

void change(int p,ll tag1,ll tag2){
  if(!p) return;
  int id=tr[p].id;
  ans1[id]=max(ans1[id],tag1);
  ans2[id]=max(ans2[id],tag2);
  tr[p].tag1=max(tr[p].tag1,tag1);
  tr[p].tag2=max(tr[p].tag2,tag2);
}

void pd(int p){
  if(!p) return ;
  if(lc(p)) change(lc(p),tr[p].tag1,tr[p].tag2);
  if(rc(p)) change(rc(p),tr[p].tag1,tr[p].tag2);
  tr[p].tag1=tr[p].tag2=0;
}

int nwnode(ll val,int id){
  int it=0;
  if(!q.empty()) it=q.front(),q.pop();
  else it=++idx;
  tr[it].id=id;
  tr[it].key=rd();
  tr[it].mx=tr[it].val=val;
  tr[it].siz=1;
  tr[it].se=-inf;
  tr[it].s[0]=tr[it].s[1]=tr[it].tag1=tr[it].tag2=0;
  return it;
}

void split(int p,int k,int &x,int &y){
  if(!p){x=y=0;return;}
  pd(p);
  if(tr[p].id<=k) x=p,split(rc(p),k,rc(p),y);
  else y=p,split(lc(p),k,x,lc(p));
  pu(p);
}

int merge(int x,int y){
  if(!x||!y) return (x|y);
  pd(x);pd(y); 
  if(tr[x].key<tr[y].key){
    tr[x].s[1]=merge(tr[x].s[1],y);
	pu(x);return x; 
  }
  else {
  	tr[y].s[0]=merge(x,tr[y].s[0]);
  	pu(y);return y;
  }
}

void del(int id){
  int &rot=rt[w[id]];
  split(rot,id,x,z);
  split(x,id-1,x,y);
  q.push(y);
  y=merge(lc(y),rc(y));
  rot=merge(merge(x,y),z);
}

void ins(int id,int p){
  int &rot=rt[p];
  ans1[id]=max(ans1[id],tr[rot].mx);
  ans2[id]=max(ans2[id],1ll*tr[rot].siz);
  change(rot,a[id].w,tr[rot].siz);
  split(rot,id,x,y);
  rot=merge(merge(x,nwnode(a[id].w,id)),y);
  w[id]=p;
}

void dfs(int p){
  if(!p) return;
  pd(p);
  if(lc(p)) dfs(lc(p));
  if(rc(p)) dfs(rc(p));
}

int main(){
  /*2023.10.25 H_W_Y P4200 千山鸟飞绝 FHQ-Treap*/
  n=read();
  for(int i=1;i<=n;i++){
  	a[i].w=1ll*read(),a[i].x=1ll*read(),a[i].y=1ll*read();
  	pos[i]=a[i].pos=a[i].x*inf+a[i].y;
  }
  m=read();
  for(int i=1;i<=m;i++){
  	b[i].w=read(),b[i].x=1ll*read(),b[i].y=1ll*read();
  	pos[i+n]=b[i].pos=b[i].x*inf+b[i].y;
  }
  sort(pos+1,pos+n+m+1);
  int len=unique(pos+1,pos+n+m+1)-pos-1;
  for(int i=1;i<=n;i++) a[i].pos=lower_bound(pos+1,pos+len+1,a[i].pos)-pos;
  for(int i=1;i<=m;i++) b[i].pos=lower_bound(pos+1,pos+len+1,b[i].pos)-pos;
  for(int i=1;i<=n;i++) ins(i,a[i].pos);
  for(int i=1;i<=m;i++) del(b[i].w),ins(b[i].w,b[i].pos);
  for(int i=1;i<=len;i++) dfs(rt[i]);
  for(int i=1;i<=n;i++) print(ans1[i]*ans2[i]);
  return 0;
}

CF702F T-Shirts

Statement

n 种 T 恤,每种有价格 ci 和品质 qi

m 个人要买 T 恤,第 i 个人有 vi 元,每人每次都会买一件能买得起的 qi 最大的 T 恤。一个人只能买一种 T 恤一件,所有人之间都是独立的。

问最后每个人买了多少件 T 恤?如果有多个 qi 最大的 T 恤,会从价格低的开始买。

1n,m2×105 ,时限 4s

Solution

首先不难想到对于 n 种 T 恤,我们按照 qi 从大到小排序,

如果 qi 相同则按照 ci 从小到大排序。

对于每一个人,我们可以从前往后遍历一遍,能买就买了。

现在考虑是否可以对于每一种 T恤,我们对整个序列进行操作,

具体来说也就是把 ci 的数全部减去 ci,并且 ans+1

发现这样并不能保证序列的单调性,

中间在 cival<2ci 的数有可能产生冲突,

那怎么办呢?

由于 cival<2ci ,那么 ci>val2

所以对于这样的 val ,我们直接进行暴力的删除和插入,

因为这样的操作最多进行 logval 次,每次都会减小到原来的一半。

时限是 4s ,足以通过,用平衡树维护即可。

时间复杂度 O((n+log2qi)log2n)

注意标记要清空!!!

#include <bits/stdc++.h>
using namespace std;

const int N=2e5+5;
int n,idx=0,rt,m,ans[N],val[N],x,y,z;
struct node{
  int c,q;
  bool operator <(const node &rhs) const{
    if(q!=rhs.q) return q>rhs.q;
    return c<rhs.c;
  }
}a[N];
struct Treap{
  int siz,val,id,s[2],key,tag,add;
}tr[N];
mt19937 rd(time(0));
queue<int> q; 

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

void print(int x){
  int p[15],tmp=0;
  if(x==0) putchar('0');
  if(x<0) putchar('-'),x=-x;
  while(x){
    p[++tmp]=x%10;
    x/=10;
  } 
  for(int i=tmp;i>=1;i--) putchar(p[i]+'0');
  putchar(' ');
}

#define lc(p) tr[p].s[0]
#define rc(p) tr[p].s[1]

int nwnode(int val,int id){
  int it;
  if(!q.empty()) it=q.front(),q.pop();
  else it=++idx;
  tr[it].val=val;
  tr[it].id=id;
  tr[it].key=rd();
  tr[it].siz=1;
  tr[it].s[0]=tr[it].s[1]=tr[it].tag=tr[it].add=0;
  return it;
}

void pu(int p){tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1;}

void change(int p,int tag,int add){
  if(!p) return ;
  tr[p].tag+=tag;
  tr[p].add+=add;
  ans[tr[p].id]+=add;
  tr[p].val+=tag;
}

void pd(int p){
  if(tr[p].add==0) return;
  if(lc(p)) change(lc(p),tr[p].tag,tr[p].add);
  if(rc(p)) change(rc(p),tr[p].tag,tr[p].add);
  tr[p].tag=0;tr[p].add=0;
}

void split(int p,int k,int &x,int &y){
  if(!p){x=y=0;return;}
  pd(p);
  if(tr[p].val<=k) x=p,split(rc(p),k,rc(p),y);
  else y=p,split(lc(p),k,x,lc(p));
  pu(p); 
}

int merge(int x,int y){
  if(!x||!y) return (x|y);
  pd(x);pd(y);
  if(tr[x].key<tr[y].key){
  	tr[x].s[1]=merge(tr[x].s[1],y);
  	pu(x);return x;
  }
  else{
  	tr[y].s[0]=merge(x,tr[y].s[0]);
  	pu(y);return y;
  }
}

void ins(int &p,int val,int id){
  int xx=0,yy=0;
  split(p,val,xx,yy);
  p=merge(merge(xx,nwnode(val,id)),yy);
}

void dfs(int p,int c){
  if(!p) return ;
  pd(p); 
  if(lc(p)) dfs(lc(p),c);
  if(rc(p)) dfs(rc(p),c);
  q.push(p);ans[tr[p].id]++;
  if(tr[p].val-c>0) ins(x,tr[p].val-c,tr[p].id);
}

void update(int c){
  split(rt,c-1,x,y);
  split(y,2*c,y,z);
  if(z) change(z,-c,1);
  dfs(y,c);
  rt=merge(x,z);
}

void getans(int p){
  pd(p);
  if(lc(p)) getans(lc(p));
  if(rc(p)) getans(rc(p));
}

int main(){
  /*2023.10.25 H_W_Y CF702F T-Shirts FHQ-Treap*/
  n=read();
  for(int i=1;i<=n;i++) a[i].c=read(),a[i].q=read();
  sort(a+1,a+n+1);
  m=read();
  for(int i=1;i<=m;i++) val[i]=read(),ins(rt,val[i],i);
  for(int i=1;i<=n;i++) update(a[i].c);
  getans(rt);
  for(int i=1;i<=m;i++) print(ans[i]);
  return 0;
}

CF809D Hitchhiking in the Baltic States

Statement

给出 n 个区间 [li,ri]n 个未知数 a1,a2,,an,现在你要确定这 n 个数,使得 ai[li,ri],并且这个序列的最长严格上升子序列尽可能大,求这个最大值。

1n3×1051li,ri109

Solution

好题!

见这里

P3835 【模板】可持久化平衡树

Statement

您需要写一种数据结构(可参考题目标题),来维护一个可重整数集合,其中需要提供以下操作( 对于各个以往的历史版本 ):

1、 插入 x

2、 删除 x(若有多个相同的数,应只删除一个,如果没有请忽略该操作

3、 查询 x 的排名(排名定义为比当前数小的数的个数 +1

4、查询排名为 x 的数

5、 求 x 的前驱(前驱定义为小于 x,且最大的数,如不存在输出 231+1

6、求 x 的后继(后继定义为大于 x,且最小的数,如不存在输出 2311

对于 100% 的数据, 1n5×105 , |xi|1090vi<i1opt6

Solution

顾名思义,就是可持久化的平衡树。

一般使用 FHQ -Treap完成,和普通平衡树的板子没有什么太大区别,

就是在 Split 的时候加上了新建一个节点的操作,再维护一下每一个状态的根即可。

#include <bits/stdc++.h>
using namespace std;
mt19937 rd(time(0));
#define ll long long

const int N=5e5+5;
int n,m,idx=0,rt[N];
struct Treap{
  int s[2],siz,key;
  ll val;
}tr[N*50];

int nwnode(ll v=0){
  tr[++idx].val=v;
  tr[idx].key=rd();
  tr[idx].siz=1;
  tr[idx].s[0]=tr[idx].s[1]=0;
  return idx;
}

#define lc(p) tr[p].s[0]
#define rc(p) tr[p].s[1]

void pushup(int p){tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1;}

int merge(int x,int y){
  if(!x||!y) return (x|y);
  if(tr[x].key<tr[y].key){
  	tr[x].s[1]=merge(tr[x].s[1],y);
  	pushup(x);return x;
  }
  else{
  	tr[y].s[0]=merge(x,tr[y].s[0]);
  	pushup(y);return y;
  }
}

void split(int p,ll k,int &x,int &y){
  if(!p){x=y=0;return;}
  if(tr[p].val<=k){
  	x=nwnode();
  	tr[x]=tr[p];
  	split(tr[x].s[1],k,tr[x].s[1],y);
  	pushup(x);
  }
  else{
  	y=nwnode();
  	tr[y]=tr[p];
  	split(tr[y].s[0],k,x,tr[y].s[0]);
  	pushup(y);
  }
}

int find(int p,int k){
  while(1){
  	if(k<=tr[lc(p)].siz) p=lc(p);
  	else{
  	  if(k>tr[lc(p)].siz+1) k-=tr[lc(p)].siz+1,p=rc(p);
	  else return p;	
	}
  }
}

int main(){
  /*2023.10.23 H_W_Y P3835 【模板】可持久化平衡树 FHQ-Treap*/ 
  scanf("%d",&n);
  for(int i=1;i<=n;i++){
  	int x=0,y=0,z=0,op,tmp;ll val;
  	scanf("%d%d%lld",&tmp,&op,&val);
  	rt[i]=rt[tmp];
  	if(op==1){
  	  split(rt[i],val,x,y);
	  rt[i]=merge(merge(x,nwnode(val)),y); 
	}
	if(op==2){
	  split(rt[i],val,x,z);
	  split(x,val-1,x,y);
	  y=merge(tr[y].s[0],tr[y].s[1]);
	  rt[i]=merge(merge(x,y),z);
	}
	if(op==3){
	  split(rt[i],val-1,x,y);
	  printf("%d\n",tr[x].siz+1);
	  rt[i]=merge(x,y);
	}
	if(op==4){
	  printf("%lld\n",tr[find(rt[i],val)].val);
	}
	if(op==5){
	  split(rt[i],val-1,x,y);
	  if(x==0) printf("-2147483647\n");
	  else printf("%lld\n",tr[find(x,tr[x].siz)].val),rt[i]=merge(x,y);
	}
	if(op==6){
	  split(rt[i],val,x,y);
	  if(y==0) printf("2147483647\n");
	  else printf("%lld\n",tr[find(y,1)].val),rt[i]=merge(x,y);
	}
  }
  return 0;
}

P5055 【模板】可持久化文艺平衡树

Statement

您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):

  1. 在第 p 个数后插入数 x
  2. 删除第 p 个数。
  3. 翻转区间 [l,r],例如原序列是 {5,4,3,2,1},翻转区间 [2,4] 后,结果是 {5,2,3,4,1}
  4. 查询区间 [l,r] 中所有数的和。

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 4 即保持原版本无变化),新版本即编号为此次操作的序号。

本题强制在线。

对于 100% 的数据,1n2×105|xi|<106

Solution

同样是在每一次 split 的时候每次复制一个节点。

注意 lstans 是 long long,所以 l,r 都要开 long long。

#include <bits/stdc++.h>
using namespace std;
mt19937 rd(time(0));
#define ll long long

const int N=2e5+5;
int n,idx=0,rt[N];
ll lstans=0;
struct Treap{
  int key,s[2],siz,tag;
  ll val,sum;
}tr[N<<7];

int nwnode(ll v=0){
  idx++;
  tr[idx].val=tr[idx].sum=v;
  tr[idx].key=rd();
  tr[idx].siz=1;
  tr[idx].s[0]=tr[idx].s[1]=tr[idx].tag=0;
  return idx;
}

#define lc(p) tr[p].s[0]
#define rc(p) tr[p].s[1]

void pu(int p){
  tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1;
  tr[p].sum=tr[lc(p)].sum+tr[rc(p)].sum+tr[p].val;  
}

int cn(int p){
  int x=nwnode();
  tr[x]=tr[p];
  return x;
}

void pd(int p){
  if(!tr[p].tag) return ;
  if(lc(p)) lc(p)=cn(lc(p));
  if(rc(p)) rc(p)=cn(rc(p));
  swap(lc(p),rc(p));
  if(lc(p)) tr[lc(p)].tag^=1;
  if(rc(p)) tr[rc(p)].tag^=1;
  tr[p].tag=0;
}

void split(int p,int k,int &x,int &y){
  if(!p){x=y=0;return;}
  pd(p);
  if(tr[lc(p)].siz<k){
  	x=cn(p);
  	split(rc(x),k-tr[lc(x)].siz-1,rc(x),y);
  	pu(x);
  }
  else {
  	y=cn(p);
  	split(lc(y),k,x,lc(y));
    pu(y);
  }
}

int merge(int x,int y){
  if(!x||!y) return (x|y);
  pd(x);pd(y);
  if(tr[x].key<tr[y].key){
  	tr[x].s[1]=merge(tr[x].s[1],y);
  	pu(x);return x;
  }
  else{
  	tr[y].s[0]=merge(x,tr[y].s[0]);
  	pu(y);return y;
  }
}

int read(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

int main(){
  /*2023.10.24 H_W_Y P5055 【模板】可持久化文艺平衡树 FHQ-Treap*/
  n=read();
  for(int i=1;i<=n;i++){
  	int v,opt;ll val,l,r;
  	int x=0,y=0,z=0;
  	scanf("%d%d",&v,&opt);
  	rt[i]=rt[v];
  	if(opt==1){
  	  scanf("%lld%lld",&l,&val);
  	  l^=lstans;val^=lstans; 
	  split(rt[v],l,x,y);
	  rt[i]=merge(x,merge(nwnode(val),y));
	} 
	if(opt==2){
	  scanf("%lld",&l);l^=lstans;
	  split(rt[v],l,x,z);
	  split(x,l-1,x,y);
	  rt[i]=merge(x,z);
	}
	if(opt==3){
	  scanf("%lld%lld",&l,&r);
	  l^=lstans;r^=lstans;
	  split(rt[i],r,x,z);
	  split(x,l-1,x,y);
	  tr[y].tag^=1;
	  rt[i]=merge(merge(x,y),z);
	}
	if(opt==4){
	  scanf("%lld%lld",&l,&r);
	  l^=lstans;r^=lstans;
	  split(rt[v],r,x,z);
	  split(x,l-1,x,y);
	  printf("%lld\n",lstans=tr[y].sum);
	  rt[i]=merge(merge(x,y),z); 
	}
  }
  return 0;
}

本文作者:H_W_Y

本文链接:https://www.cnblogs.com/H-W-Y/p/balancetree.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   H_W_Y  阅读(26)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起