Splay做题笔记
模板
题目描述:
辣鸡ljh NOI之后就退役了,然后就滚去学文化课了。
他每天都被katarina大神虐,仗着自己学过一些姿势就给katarina大神出了一道题。
有一棵 \(n\) 个节点的以 1 号节点为根的树,每个节点上有一个小桶,节点\(u\)上的小桶可以容纳\(k_{u}\)个小球,ljh每次可以给一个节点到根路径上的所有节点的小桶内放一个小球,如果这个节点的小桶满了则不能放进这个节点,在放完所有小球之后就企图去刁难katarina大神,让katarina大神回答每个节点的小桶内的小球有多少种颜色。
然而katarina大神一眼就秒掉了,还说这就是一道傻逼模板题。
现在katarina大神想考考即将参加NOIP2019的你能不能回答上辣鸡ljh的问题。
第一行,一个整数n,树上节点的数量。
接下来n ? 1行,每行两个整数u, v,表示在u, v之间有一条边。
接下来一行n个整数, ~ 表示每个节点上的小桶数量。
下一行是一个整数m,表示ljh进行的操作数量。
接下来m行,每行两个整数x, c,分别表示进行操作的节点和小球颜色。
下一行是一个整数Q,表示你需要回答的询问数。
接下来Q行,每行一个整数x,表示一个询问。
本来昨天就能A掉的,结果没考虑到负数的情况,快读直接跳过负号,
导致连WA n次的惨烈局面。(话说要是我不看测试点还要调多久啊。。。
教训:以后打快读不能偷懒为了卡一点小常数忽略负号了。
本题让我对\(splay\)的认识加深了许多。
首先是以修改时间为下标,修改时记录上每个点的时间,查询时查询时间区间就行了。
另外,对于每个点一开始都建一个\(splay\),其中包含\(root\)和一个\(map\)记录是否出现过这种小球。
当修改时先只修改最子叶的\(splay\),因为下面有着\(dfs\),可以将子树的状态合并到父节点上
至于合并的方法,是启发式合并,以前一直以为这是个什么特别厉害的东西,其实就是暴力把所有子树上的节点按照传统方式插入到父节点上...(父节点:size较大的点;
子结点:size小,插入简便的点——和\(splay\)上的父节点、子结点区分开来,这么说来,其实线段树也可以这么做吧
于是我们就可以在\(dfs\)的时候预处理出每个结点的答案,查询时直接输出即可。
\(splay\)和\(map\)的结合应用\(get\sqrt{}\)
还有,对于每个结点有一个\(rec\)数组,相当于记录了该节点在哪一颗\(splay\)树上,
在合并时如果合并到子树上就更改父亲的\(rec\),而合并到父亲上就不用更改了,因为子树的答案已经统计完了,修改\(rec\)只会浪费一点时间。
\(splay\)的中序遍历\(get\sqrt{}\)(因为左子树时间小,右子树时间大,父节点时间位于二者之间,所以按照左—父—右的顺序插入。
到这里蒟弱的思路就发表完毕了,下面是code:
Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
namespace EMT
{
#define F(i,a,b) for(register int i=a;i<=b;i++)
const int N=1e5+100;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
struct node{
int son[2],tim,size,cnt,val,fa,col;
}t[N<<2];
int head[N],co,num,ans1,rec[N],n,m,q,now;
struct edge{int next,to;}e[N<<1];
inline void add(int next,int to){e[++co].next=head[next],e[co].to=to,head[next]=co;}
struct tree{
map<int,int>mp;
int root;
inline int siz(){return t[root].size;}
inline int get(int x){return t[t[x].fa].son[1]==x;}
inline void up(int x){t[x].size=t[t[x].son[1]].size+t[t[x].son[0]].size+1;t[x].cnt=t[t[x].son[1]].cnt+t[t[x].son[0]].cnt+t[x].val;}
inline void res(int x){
int old=t[x].fa,oldf=t[old].fa,k=get(x);
t[old].son[k]=t[x].son[k^1];
t[t[x].son[k^1]].fa=old;
t[x].son[k^1]=old;t[old].fa=x;
if(oldf)t[oldf].son[t[oldf].son[1]==old]=x;
t[x].fa=oldf;
up(old);up(x);
}
inline void splay(int x){
for(register int fa;(fa=t[x].fa);res(x))
if(t[fa].fa)res(get(fa)==get(x)?fa:x);
root=x;
}
inline void insert(int tim,int col,int val){
int f=0,x=root;bool bg;
while((!bg)||(x&&t[x].tim!=tim))bg=1,f=x,x=t[x].son[tim>t[x].tim];
x=++num;
if(f)t[f].son[tim>t[f].tim]=x;
t[x].tim=tim;t[x].val=t[x].cnt=val;t[x].col=col;t[x].fa=f;t[x].size=1;
splay(x);
}
inline void change(int tim){
int x=root;
while(x&&t[x].tim!=tim)x=t[x].son[tim>t[x].tim];
if(x)t[x].val=0;splay(x);
}
inline int find(int tim,int col){
if(!mp[col]){
mp[col]=tim;
return 1;
}
else if(mp[col]>tim){
change(mp[col]);
mp[col]=tim;
return 1;
}
else return 0;
}
inline int findx(int x,int lim){
if(!x)return 0;
if(t[t[x].son[0]].size>=lim)findx(t[x].son[0],lim);
else if(t[t[x].son[0]].size+1>=lim)return t[t[x].son[0]].cnt+t[x].val+ans1;
else ans1+=t[t[x].son[0]].cnt+t[x].val,findx(t[x].son[1],lim-t[t[x].son[0]].size-1);
}
inline int findval(int lim){
ans1=0;
if(!lim)return 0;
if(lim>=t[root].size)return t[root].cnt;
return findx(root,lim);
}
}a[N];
inline void make(int x){
if(!x)return;
make(t[x].son[0]);
a[now].insert(t[x].tim,t[x].col,a[now].find(t[x].tim,t[x].col));
make(t[x].son[1]);
}int ans[N],k[N];
inline void dfs(int x,int fa){
for(register int i=head[x],j;i;i=e[i].next){
j=e[i].to;if(j==fa)continue;
dfs(j,x);
if(a[rec[x]].siz()<a[rec[j]].siz()){
now=rec[j];
make(a[rec[x]].root);
rec[x]=now;
}
else{
now=rec[x];
make(a[rec[j]].root);
}
}
ans[x]=a[rec[x]].findval(k[x]);
}
inline short main(){
freopen("ac7.in","r",stdin);
freopen("my.out","w",stdout);
n=read();
F(i,1,n-1){
int x=read(),y=read();add(x,y);add(y,x);
}
F(i,1,n)k[i]=read(),rec[i]=i;
m=read();
F(i,1,m){
int x=read(),y=read();
a[rec[x]].insert(i,y,a[rec[x]].find(i,y));
}
dfs(1,0);
q=read();
while(q--){
int x=read();
printf("%d\n",ans[x]);
}
return 0;
}
}
signed main() { return EMT::main();}
【模板】二逼平衡树(树套树)
树套树的新题型,又让蒟弱涨见识了。
由于蒟弱在学习\(splay\),所以考虑用线段树套\(splay\)来解决本题,
对于每一个线段树结点建立一颗\(splay\)树,修改时同时修改两颗树,
当查询时利用线段树的区间性质巧妙结合二分完成查询。
另外,对于\(splay\)和线段树各建一个操作结构体,好看方便调试。
(码量有点大的说
Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
namespace EMT
{
#define F(i,a,b) for(register int i=a;i<=b;i++)
#define D(i,a,b) for(register int i=a;i>=b;i--)
#define pf printf
#define int long long
inline void s(){pf("shit\n");}
const int N=5e5+100,maxn=2147483647;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
inline void pi(int x){pf("%lld ",x);}inline void pn(){pf("\n");}inline void ps(int a[],int size){F(i,1,size)pi(a[i]);pn();}
/*------------------------------------------------------------------------Splay------------------------------------------------------------------------------------- */
struct stree{int son[2],fa,key,size,cnt;}t[N<<2];int num;
struct Splay{
int root;
inline void reset(int x,int v){t[x].son[0]=t[x].son[1]=t[x].fa=0;t[x].size=t[x].cnt=1;t[x].key=v;}
inline void clean(int x){t[x].son[0]=t[x].son[1]=t[x].fa=t[x].size=t[x].cnt=t[x].key=0;}
inline bool get(int x){return t[t[x].fa].son[1]==x;}
inline void up(int x){
if(x){
t[x].size = t[x].cnt;
t[x].size += (t[x].son[0] ? t[t[x].son[0]].size : 0) + (t[x].son[1] ? t[t[x].son[1]].size : 0);
}
}
inline void res(int x){
int old = t[x].fa, oldf = t[old].fa, which = get(x);
t[old].son[which] = t[x].son[which ^ 1];
t[t[old].son[which]].fa = old;
t[old].fa = x;
t[x].son[which ^ 1] = old;
t[x].fa = oldf;
if(oldf)t[oldf].son[t[oldf].son[1] == old] = x;
up(old);up(x);
}
inline void splay(int x){
for (int fa; (fa = t[x].fa); res(x))
if (t[fa].fa)res((get(fa) == get(x)) ? fa : x);
root = x;
}
inline void insert(int x){
if (!root){
num++;reset(num, x);root=num;return;
}
int now = root, fa = 0;
while (1){
if (t[now].key == x){
t[now].cnt++;up(now);up(fa);
splay(now);break;
}
fa = now;
now = t[now].son[t[now].key < x];
if (!now){
num++;reset(num, x);
t[num].fa = fa;t[fa].son[t[fa].key < x] = num;
up(fa);
splay(num);
break;
}
}
}
inline int pre(){
int now = t[root].son[0];
while (t[now].son[1])now = t[now].son[1];
return now;
}
inline int nxt(){
int now = t[root].son[1];
while (t[now].son[0])now = t[now].son[0];
return now;
}
inline int find(int x){
int ans = 0, now = root;
while (now){
if (x < t[now].key)
now = t[now].son[0];
else{
ans += t[now].son[0] ? t[t[now].son[0]].size : 0;
if (x == t[now].key){
splay(now);
return ans ;
}
ans += t[now].cnt;
now = t[now].son[1];
}
}return ans;
}
inline int getpre(int v){
int now=root,ans=-maxn;
while(now){
if(t[now].key<v){ans=max(ans,t[now].key),now=t[now].son[1];}
else now=t[now].son[0];
}return ans;
}
inline int getnxt(int v){
int now=root,ans=maxn;
while(now){
if(t[now].key>v){ans=min(ans,t[now].key),now=t[now].son[0];}
else now=t[now].son[1];
}return ans;
}
inline void del(int x){
find(x);
if (t[root].cnt > 1){t[root].cnt--;up(root);return;}
if (!t[root].son[0] && !t[root].son[1]){clean(root);root = 0;return;}
if (!t[root].son[0]){
int old = root;
root = t[root].son[1];
t[root].fa = 0;
clean(old);
return;
}
else if (!t[root].son[1]){
int old = root;
root = t[root].son[0];
t[root].fa = 0;
clean(old);
return;
}
int old = root, lmax = pre();
splay(lmax);
t[t[old].son[1]].fa = root;
t[root].son[1] = t[old].son[1];
clean(old);
up(root);
}
}a[N<<2];
/*-----------------------------------------------------------------------Segment-tree---------------------------------------------------------------------------------- */
int dat[N<<2],ans;
struct se{
inline void ins(int x,int l,int r,int p,int v){
a[x].insert(v);if(l==r)return;
int mid=(l+r)>>1;
if(p<=mid)ins(x*2,l,mid,p,v);
else ins(x*2+1,mid+1,r,p,v);
}
inline void change(int x,int l,int r,int p,int to){
a[x].del(dat[p]);a[x].insert(to);
if(l==r){dat[p]=to;return;}
int mid=(l+r)>>1;
if(p<=mid)change(x*2,l,mid,p,to);
else change(x*2+1,mid+1,r,p,to);
}
inline void pre(int x,int l,int r,int L,int R,int v){
if(l==L&&r==R){ans=max(ans,a[x].getpre(v));return;}
int mid=(l+r)>>1;
if(R<=mid)pre(x*2,l,mid,L,R,v);
else if(L>mid)pre(x*2+1,mid+1,r,L,R,v);
else pre(x*2,l,mid,L,mid,v),pre(x*2+1,mid+1,r,mid+1,R,v);
}
inline void nxt(int x,int l,int r,int L,int R,int v){
if(l==L&&r==R){ans=min(ans,a[x].getnxt(v));return;}
int mid=(l+r)>>1;
if(R<=mid)nxt(x*2,l,mid,L,R,v);
else if(L>mid)nxt(x*2+1,mid+1,r,L,R,v);
else nxt(x*2,l,mid,L,mid,v),nxt(x*2+1,mid+1,r,mid+1,R,v);
}
inline void rank(int x,int l,int r,int L,int R,int v){
if(l==L&&r==R){ans+=a[x].find(v);return;}int mid=(l+r)>>1;
if(R<=mid)rank(x*2,l,mid,L,R,v);
else if(L>mid)rank(x*2+1,mid+1,r,L,R,v);
else rank(x*2,l,mid,L,mid,v),rank(x*2+1,mid+1,r,mid+1,R,v);
}
}segm;
/* -------------------------------------------------------------------------ask----------------------------------------------------------------------------------------------*/
int n,m,mx;
inline int getrank(int x,int y,int k){
int l=0,r=mx,mid;
while(l<=r){
mid=(l+r)>>1;ans=0;
segm.rank(1,1,n,x,y,mid);
if(ans<k)l=mid+1;
else if(ans==k)r=mid-1;
else r=mid-1;
}return l-1;
}
inline short main(){
// freopen("in.in","r",stdin);
// freopen("my.out","w",stdout);
n=read();m=read();
F(i,1,n){dat[i]=read();segm.ins(1,1,n,i,dat[i]);mx=max(mx,dat[i]);}
F(i,1,m){
int k,op=read(),x=read(),y=read();
if(op==1){k=read();ans=0;segm.rank(1,1,n,x,y,k);pi(ans+1);pn();}
if(op==2){k=read();pi(getrank(x,y,k));pn();}
if(op==3)segm.change(1,1,n,x,y);
if(op==4){k=read();ans=-maxn;segm.pre(1,1,n,x,y,k);pi(ans);pn();}
if(op==5){k=read();ans=maxn;segm.nxt(1,1,n,x,y,k);pi(ans);pn();}
}
return 0;
}
}
signed main() { return EMT::main();}
Splay做题笔记的更多相关文章
- LCT做题笔记
最近几天打算认真复习LCT,毕竟以前只会板子.正好也可以学点新的用法,这里就用来写做题笔记吧.这个分类比较混乱,主要看感觉,不一定对: 维护森林的LCT 就是最普通,最一般那种的LCT啦.这类题目往往 ...
- C语言程序设计做题笔记之C语言基础知识(下)
C 语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行 事.并且C是相当灵活的,用于执行计算机程序能完成的 ...
- C语言程序设计做题笔记之C语言基础知识(上)
C语言是一种功能强大.简洁的计算机语言,通过它可以编写程序,指挥计算机完成指定的任务.我们可以利用C语言创建程序(即一组指令),并让计算机依指令行事.并且C是相当灵活的,用于执行计算机程序能完成的几乎 ...
- SDOI2017 R1做题笔记
SDOI2017 R1做题笔记 梦想还是要有的,万一哪天就做完了呢? 也就是说现在还没做完. 哈哈哈我竟然做完了-2019.3.29 20:30
- SDOI2014 R1做题笔记
SDOI2014 R1做题笔记 经过很久很久的时间,shzr又做完了SDOI2014一轮的题目. 但是我不想写做题笔记(
- SDOI2016 R1做题笔记
SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...
- java做题笔记
java做题笔记 1. 初始化过程是这样的: 1.首先,初始化父类中的静态成员变量和静态代码块,按照在程序中出现的顺序初始化: 2.然后,初始化子类中的静态成员变量和静态代码块,按照在程序中出现的顺序 ...
- SAM 做题笔记(各种技巧,持续更新,SA)
SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...
- PKUWC/SC 做题笔记
去年不知道干了些啥,什么省选/营题都没做. 现在赶应该还来得及(?) 「PKUWC2018」Minimax Done 2019.12.04 9:38:55 线段树合并船新玩法??? \(O(n^2)\ ...
随机推荐
- ESP32构建系统(CMake版)
ESP32 芯片是一款 2.4 GHz Wi-Fi 和蓝牙双模芯片,内置 1 或 2 个 32 位处理器,运算能力最高可达 600 DMIPS. ESP-IDF 即乐鑫物联网开发框架,可为在 Wind ...
- python使用笔记29--代码驱动
1 import unittest 2 import requests 3 import jsonpath 4 import time 5 import nnreport 6 7 def get_va ...
- printf函数返回值
//返回值:正确返回输出的字符总数,错误返回负值,与此同时,输入输出流错误标志将被置值,可由指示器ferror来检查输入输出流的错误标志. #include <stdio.h> #defi ...
- MQTT 3 ——MQTT与Spring Mvc整合
本篇记录一下MQTT客户端与Spring Mvc整合 网络上已经有很多的MQTT客户端与SpringBoot整合的技术文档,但是与Spring Mvc框架的整合文档似乎并不太多,可能是因为Spri ...
- Spark—RDD编程常用转换算子代码实例
Spark-RDD编程常用转换算子代码实例 Spark rdd 常用 Transformation 实例: 1.def map[U: ClassTag](f: T => U): RDD[U] ...
- linux查看电脑温度
sudo apt-get install lm-sensors # 安装yes | sudo sensors-detect # 侦测所有感测器 sensors # 查看温度
- ArcGis API JS 4.X本地化部署与地图的基础使用
准备工作 首先下载ArcGIS API for JavaScript4.x,这里下载的是4.19. 官方下载地址:https://developers.arcgis.com/downloads/ ar ...
- 就这?一篇文章让你读懂 Spring 事务
什么是事务 ▲ 百度百科 概括来讲,事务是一个由有限操作集合组成的逻辑单元.事务操作包含两个目的,数据一致以及操作隔离.数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效:事务回滚 ...
- 【洛谷P1816 忠诚】线段树
题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意.但是由于一些人的挑拨,财主还是对管家产生了 ...
- (Opencv07)绘制边框
(Opencv07)绘制边框 cv2.boundingRect(img) 这个函数可以获得一个图像的最小矩形边框一些信息 cv2.rectangle()可以画出该最小矩形边框 x, y ,w, h = ...