CF702F T-Shirts
对于每个人剩下的钱建平衡树,那么我们按照优先级对于每个物品
(
c
,
p
)
(c,p)
(c,p),将钱
≥
c
\geq c
≥c的人的钱
−
=
c
-=c
−=c,答案
+
+
++
++,区间打标记即可,注意到用非旋
t
r
e
a
p
treap
treap维护时
c
≤
x
<
2
c
c\leq x \lt 2c
c≤x<2c的物品无法直接
m
e
r
g
e
merge
merge,拿出来暴力插入即可,可以证明每次
x
x
x的权值会缩小至少一半,所以暴力插入次数是
O
(
n
log
x
)
O(n\log x)
O(nlogx)的。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 200005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,m;
int lc[maxn],rc[maxn],val[maxn],key[maxn],ad[maxn],ans[maxn],ada[maxn],rt;
void dt(int u){
if(!u) return;
if(ad[u]){
val[u] += ad[u];
ad[lc[u]] += ad[u];
ad[rc[u]] += ad[u];
ad[u] = 0;
}
if(ada[u]){
ans[u] += ada[u];
ada[lc[u]] += ada[u];
ada[rc[u]] += ada[u];
ada[u] = 0;
}
}
void merge(int &u,int a,int b){
if(!a || !b) return (void)(u = a+b);
dt(a),dt(b);
if(key[a] < key[b]) u = a , merge(rc[u],rc[a],b);
else u = b , merge(lc[u],a,lc[b]);
}
void split(int u,int &a,int &b,int v){
if(!u) return (void)(a = b = 0);
dt(u);
if(val[u] >= v) b = u , split(lc[u],a,lc[b],v);
else a = u , split(rc[u],rc[a],b,v);
}
int C[maxn],q[maxn],c[maxn];
bool cmp(const int &u,const int &v){ return q[u] == q[v] ? C[u] < C[v] : q[u] > q[v]; }
void ins(int &u,int v){
int a,b;
split(u,a,b,val[v]);
merge(u,a,v),merge(u,u,b);
}
void dfs(int &u,int &v){
if(!u) return;
dt(u);
dfs(lc[u],v),dfs(rc[u],v);
ins(v,u);
u = 0;
}
void dfs2(int u){
if(!u) return;
dt(u);
dfs2(lc[u]),dfs2(rc[u]);
}
int main(){
scanf("%d",&n);
rep(i,1,n) scanf("%d%d",&C[i],&q[i]),c[i]=i;
sort(c+1,c+1+n,cmp);
scanf("%d",&m);
rep(i,1,m){
scanf("%d",&val[i]);
key[i] = rand() << 15 | rand();
ins(rt,i);
}
rep(i,1,n){
int u = c[i];
int a , b , c;
split(rt,a,b,C[u]);
ad[b] -= C[u] , ada[b] ++;
split(b,b,c,C[u]);
dfs(b,a);
merge(rt,a,c);
}
dfs2(rt);
rep(i,1,m) printf("%d%c",ans[i]," \n"[i==m]);
}
LOJ#120. 持久化序列
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 300005
#define maxp maxn * 100
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int rt[maxn];
int lc[maxp],rc[maxp],val[maxp],key[maxp],sz[maxp],tot;
void upd(int u){ sz[u] = sz[lc[u]] + sz[rc[u]] + 1; }
int newnode(int u){
lc[++tot] = lc[u] , rc[tot] = rc[u] ,val[tot] = val[u] , key[tot] = key[u] , sz[tot] = sz[u];
return tot;
}
void split(int u,int &a,int &b,int rk){
if(!u) return (void)(a=b=0);
u = newnode(u);
if(sz[lc[u]] + 1 <= rk) a = u , split(rc[u],rc[a],b,rk-sz[lc[u]]-1);
else b = u , split(lc[u],a,lc[b],rk);
upd(u);
}
void merge(int &u,int a,int b){
if(!a || !b) return (void)(u=a+b);
if(key[a] < key[b]) u = newnode(a) , merge(rc[u] , rc[a] , b);
else u = newnode(b) , merge(lc[u] , a , lc[b]);
upd(u);
}
int qry(int u,int rk){
for(;u;){
if(sz[lc[u]] + 1 == rk) return val[u];
if(sz[lc[u]] + 1 < rk) rk -= sz[lc[u]] + 1 , u = rc[u];
else u = lc[u];
}
return -1;
}
int main(){
int n,tim=0;
read(n);
rep(i,1,n){
int op,t,k,x;
read(op),read(t),read(k);
if(op == 1){
read(x);
int a,b;
rt[++tim] = rt[t];
split(rt[tim],a,b,k-1);
int u = newnode(0);
val[u] = x , key[u] = rand() << 15 | rand() , sz[u] = 1;
merge(rt[tim],a,u);
merge(rt[tim],rt[tim],b);
}
if(op == 2){
int a,b,c;
rt[++tim] = rt[t];
split(rt[tim],b,c,k);
split(b,a,b,k-1);
merge(rt[tim],a,c);
}
if(op == 3)
printf("%d\n",qry(rt[t],k));
}
}
LOJ #6203. 可持久化队列
要求在线:操作树上倍增即可。不知道那个O(n)的算法是什么鬼
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define ui unsigned int
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(ui &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
#define lim 22
ui T,ty,f[maxn][lim],sz[maxn],x[maxn];
int main(){
read(T),read(ty);
ui ans = 0 , op , ver , t;
rep(i,1,T){
read(op) , read(ver);
ver ^= ans * ty;
if(x[ver] == -1) memcpy(f[i],f[ver],sizeof f[i]);
else{
f[i][0] = ver;
rep(j,1,lim-1) f[i][j] = f[f[i][j-1]][j-1];
}
if(op == 1){
read(t);t ^= ans * ty;
x[i] = t;sz[i] = sz[ver] + 1;
}
else{
x[i] = -1;sz[i] = sz[ver] - 1;
int u = i;
per(j,lim-1,0) if(sz[ver] >> j & 1)
u = f[u][j];
ans = ans * 31 + x[u];
}
}
printf("%u\n",ans);
}
HDU 6087 Rikka with Sequence
可持久化
t
r
e
a
p
treap
treap的复制操作。
神乎其技的回收点操作。(把空间复杂度保持在线性是真的离谱,但是还是要用
60
M
B
60MB
60MB)
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 200005
#define maxp maxn * 5
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
using namespace std;
inline int Ran(){
static int G = 1;
return G = 3ll * G % 998244353;
}
char Start;
int n,m;
int lc[maxp],rc[maxp],sz[maxp],val[maxp],tot,del[maxp],usd[maxp],rt,rta;
LL sm[maxp];
void upd(int u){ sm[u] = sm[lc[u]] + sm[rc[u]] + val[u] , sz[u] = sz[lc[u]] + sz[rc[u]] + 1;}
void split(int u,int &a,int &b,int rk){
if(!u) return (void)(a = b = 0);
if(sz[lc[u]] + 1 <= rk) a = u , split(rc[u] , rc[a] , b , rk - sz[lc[u]] - 1);
else b = u , split(lc[u] , a , lc[b] , rk);
upd(u);
}
void merge(int &u,int a,int b){
if(!a || !b) return (void)(u = a+b);
if(Ran() % (sz[a] + sz[b]) < sz[a]) u = a , merge(rc[u],rc[a],b);
else u = b , merge(lc[u],a,lc[b]);
upd(u);
}
int newnode(int u){
int v;
if(del[0]) v = del[del[0]--];
else v = ++tot;
lc[v] = lc[u] , rc[v] = rc[u] , sz[v] = sz[u] , val[v] = val[u] , sm[v] = sm[u];
usd[++usd[0]] = v;
return v;
}
void split_r(int u,int &a,int &b,int rk){
if(!u) return (void)(a = b = 0);
u = newnode(u);
if(sz[lc[u]] + 1 <= rk) a = u , split_r(rc[u] , rc[a] , b , rk - sz[lc[u]] - 1);
else b = u , split_r(lc[u] , a , lc[b] , rk);
upd(u);
}
void merge_r(int &u,int a,int b){
if(!a || !b) return (void)(u = a+b);
if(Ran() % (sz[a] + sz[b]) < sz[a]) u = newnode(a) , merge_r(rc[u],rc[a],b);
else u = newnode(b) , merge_r(lc[u],a,lc[b]);
upd(u);
}
int vis[maxp],tim;
void dfs(int u){
if(!u) return;
vis[u] = tim;
dfs(lc[u]) , dfs(rc[u]);
}
void reuse(){
assert(tot - del[0] >= maxp - maxn);
++tim;
dfs(rt),dfs(rta);
int cnt = 0;
rep(i,1,usd[0])
if(vis[usd[i]] != tim)
del[++del[0]] = usd[i];
else
usd[++cnt] = usd[i];
usd[0] = cnt;
}
void dfs0(int &u,int v){
if(!v) return (void)(u = 0);
u = newnode(v);
dfs0(lc[u] , lc[v]) , dfs0(rc[u] , rc[v]);
}
char End;
int main(){
scanf("%d%d",&n,&m);
rep(i,1,n){
++tot;usd[++usd[0]] = tot;
scanf("%d",&val[i]);
upd(i);
merge(rta,rta,i);
}
dfs0(rt,rta);
for(int op,l,r,k,a,b,c,d,e,f;m--;){
scanf("%d%d%d",&op,&l,&r);
if(op == 1){
split_r(rt,b,c,r);
split_r(b,a,b,l-1);
printf("%lld\n",sm[b]);
merge_r(rt,a,b),merge_r(rt,rt,c);
if(tot - del[0] >= maxp - maxn)
reuse();
}
if(op == 2){
scanf("%d",&k);
split_r(rt,b,c,l-1);
split_r(c,c,e,r-l+1);
split_r(b,a,b,l-k-1);
l -= k;
for(;k <= r - l + 1;k<<=1)
merge_r(b,b,b);
split_r(b,b,d,r-l+1);
merge_r(rt,a,b);
merge_r(rt,rt,e);
if(tot - del[0] >= maxp - maxn)
reuse();
}
if(op == 3){
split_r(rta,b,c,r);
split_r(b,a,b,l-1);
split_r(rt,e,f,r);
split_r(e,d,e,l-1);
merge_r(rt,d,b);
merge_r(rt,rt,f);
merge_r(rta,a,b);
merge_r(rta,rta,c);
if(tot - del[0] >= maxp - maxn)
reuse();
}
}
}
LOJ #3325. 「SNOI2020」区间和
本来以为是个高论,但是感觉有点离谱,脑筋急转弯题
忽略可持久化,这个不是本题的重点。
对于
0
0
0操作,
可以看出如果
x
x
x的水面高度
≥
h
\geq h
≥h,则不需要操作,
否则我们直接求出
x
x
x左边和右边第一个高度
≥
h
\geq h
≥h的挡板,
把这两个挡板之间的水面高度赋值为
h
h
h即可。
线段树二分+区间赋值。
对于
1
1
1操作,
可以求出
x
x
x的当前水面高度
h
h
h,
直接求出
x
x
x左边和右边第一个高度
≥
h
\geq h
≥h的挡板,分别为
L
,
R
L,R
L,R。
则对于
[
x
,
R
]
[x,R]
[x,R]的每一个点
y
y
y,
y
y
y的水面高度会变成
[
x
,
y
]
[x,y]
[x,y]中挡板高度的最大值。
[
L
,
x
]
[L,x]
[L,x]同理。
我们发现这是和前缀最大值有关的修改
该不会要写李超树把?该不会要把单调栈预处理出来写可持久化平衡树吧,不会吧不会吧
稍微思考一下发现这个操作可以简单打懒标记维护。
就
t
a
g
l
=
x
tagl = x
tagl=x表示从左到右,该区间之外的挡板的最大值是
x
x
x的修改,
那么传下去给左儿子就是
t
a
g
l
l
c
=
x
tagl_{lc} = x
tagllc=x,传给右儿子就是
t
a
g
l
r
c
=
max
(
x
,
m
x
l
c
)
tagl_{rc} = \max(x,mx_{lc})
taglrc=max(x,mxlc)
也就是维护一下左儿子里面挡板的最高值。
因为只需要单点查询所以我们一直下放标记即可。
对于
2
2
2操作,没什么好说的,需要注意挡板是在格子之间的,用维护格子的线段树同时维护挡板高度需要一定的技巧。
对于
3
3
3操作,不实现的话你
1
1
1操作也写不了。
注意可持久化线段树是可以下放标记的,比较优秀的一种写法是每次直接把根变为新节点,递归的时候下放标记前直接把左右儿子变成新节点即可。
时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
A C C o d e \mathcal AC \ Code AC Code
//Freopen 讨厌写线段树上二分
#include<bits/stdc++.h>
#define maxn 200005
#define maxp maxn * 80
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define inf 0x3f3f3f3f
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int n,q,h[maxn],rt[maxn];
int mxh[maxp],mxhr[maxp],fz[maxp],lz[maxp],rz[maxp],lc[maxp],val[maxp],rc[maxp],tot;
#define il inline
il void dtf(int u,int v){
if(u)
fz[u] = val[u] = v;
}
il void dtl(int u,int v){
if(u)
fz[u] = -1,
lz[u] = val[u] = v;
}
il void dtr(int u,int v){
if(u)
fz[u] = lz[u] = -1,
rz[u] = val[u] = v;
}
il int newnode(int u){
fz[++tot] = fz[u] , lz[tot] = lz[u] , rz[tot] = rz[u];
mxh[tot] = mxh[u] , mxhr[tot] = mxhr[u],
lc[tot] = lc[u] , rc[tot] = rc[u] , val[tot] = val[u];
return tot;
}
il void dt(int u){
lc[u] = newnode(lc[u]);
rc[u] = newnode(rc[u]);
if(rz[u] != -1){
dtr(rc[u],rz[u]) , dtr(lc[u],max(rz[u],mxhr[rc[u]]));
rz[u] = -1;
}
if(lz[u] != -1){
dtl(lc[u],lz[u]) , dtl(rc[u],max(lz[u],mxh[lc[u]]));
lz[u] = -1;
}
if(fz[u] != -1){
dtf(lc[u],fz[u]) , dtf(rc[u],fz[u]);
fz[u] = -1;
}
}
il void upd(int u){
mxh[u] = max(mxh[lc[u]] , mxh[rc[u]]);
mxhr[u] = max(mxhr[lc[u]] , mxhr[rc[u]]);
}
void Build(int &u,int l,int r){
u = ++tot;
if(l == r) return (void)(mxh[u] = h[l] , mxhr[u] = h[l-1]);
int m = l+r>>1;
Build(lc[u],l,m) , Build(rc[u],m+1,r);
upd(u);
}
int qryl2(int u,int l,int r,int h){
if(l == r) return l;
int m = l+r>>1;
if(mxh[rc[u]] >= h) return qryl2(rc[u],m+1,r,h);
return qryl2(lc[u],l,m,h);
}
int qryl(int u,int l,int r,int p,int h){
if(l > p) return 0;
if(r <= p) return mxh[u] < h ? 0 : qryl2(u,l,r,h);
int m = l+r>>1 , t;
if(t = qryl(rc[u],m+1,r,p,h)) return t;
return qryl(lc[u],l,m,p,h);
}
int qryr2(int u,int l,int r,int h){
if(l == r) return l;
int m = l+r>>1;
if(mxhr[lc[u]] >= h) return qryr2(lc[u],l,m,h);
return qryr2(rc[u],m+1,r,h);
}
int qryr(int u,int l,int r,int p,int h){
if(r < p) return n+1;
if(l >= p) return mxhr[u] < h ? n+1 : qryr2(u,l,r,h);
int m = l+r>>1 , t;
if((t = qryr(lc[u],l,m,p,h)) <= n) return t;
return qryr(rc[u],m+1,r,p,h);
}
void insf(int &u,int l,int r,int ql,int qr,int h){
if(l>qr||ql>r) return;
if(ql<=l&&r<=qr) return (void)(dtf(u,h));
int m = l+r>>1;dt(u);
insf(lc[u],l,m,ql,qr,h),insf(rc[u],m+1,r,ql,qr,h);
}
void insl(int &u,int l,int r,int ql,int qr,int &h){
if(l>qr||ql>r) return;
if(ql<=l&&r<=qr) return (void)(dtl(u,h),h=max(h,mxh[u]));
int m = l+r>>1;dt(u);
insl(lc[u],l,m,ql,qr,h) , insl(rc[u],m+1,r,ql,qr,h);
}
void insr(int &u,int l,int r,int ql,int qr,int &h){
if(l>qr||ql>r) return;
if(ql<=l&&r<=qr) return (void)(dtr(u,h),h=max(h,mxhr[u]));
int m = l+r>>1;dt(u);
insr(rc[u],m+1,r,ql,qr,h) , insr(lc[u],l,m,ql,qr,h);
}
void ins1(int &u,int l,int r,int p,int h){
if(l == r) return (void)(mxh[u] = h);
int m = l+r>>1;dt(u);
p <= m ? ins1(lc[u],l,m,p,h) : ins1(rc[u],m+1,r,p,h);
upd(u);
}
void ins2(int &u,int l,int r,int p,int h){
if(l==r) return (void)(mxhr[u] = h);
int m = l+r>>1;dt(u);
p <= m ? ins2(lc[u],l,m,p,h) : ins2(rc[u],m+1,r,p,h);
upd(u);
}
int qry(int u,int l,int r,int p){
if(l == r) return val[u];
int m = l+r>>1;dt(u);
return p <= m ? qry(lc[u],l,m,p) : qry(rc[u],m+1,r,p);
}
int main(){
memset(fz,-1,sizeof fz) ,
memset(lz,-1,sizeof lz) ,
memset(rz,-1,sizeof rz);
read(n),read(q);
rep(i,1,n-1) read(h[i]);
h[0] = h[n] = inf;
Build(rt[0],1,n);
int op,p,x,h;
rep(i,1,q){
read(op),read(p),read(x);
rt[i] = newnode(rt[p]);
if(op == 0){
read(h);
if(qry(rt[i],1,n,x) < h){
int L = qryl(rt[i],1,n,x-1,h) , R = qryr(rt[i],1,n,x+1,h);
insf(rt[i],1,n,L+1,R-1,h);
}
}
if(op == 1){
h = qry(rt[i],1,n,x);
int L = qryl(rt[i],1,n,x-1,h) , R = qryr(rt[i],1,n,x+1,h);
insl(rt[i],1,n,x,R-1,h=0),insr(rt[i],1,n,L+1,x,h=0);
}
if(op == 2){
read(h);
ins1(rt[i],1,n,x,h) , ins2(rt[i],1,n,x+1,h);
}
if(op == 3)
printf("%d\n",qry(rt[i],1,n,x));
}
}