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)\ ...
随机推荐
- uni-app app端 人脸识别
在听到人脸识别,哇塞!感觉来个个高大上的,去阿里 腾讯 看他们的人脸识别方法,官方sdk什么的. 到后来,需求确定了,拍照(照片)上传,后台去识别是不是本人,这一瞬间从天堂到地狱,放着官方那么好的方法 ...
- 使用xcode实现IM的那些坑
想用xcode基于XMPP实现即时通讯,mac必须安装openfire(xmpp服务器),mysql(本地数据库,用于配置openfire),JDK(打开openfire必须本地具备java环境),x ...
- Java核心基础第4篇-Java数组的常规操作
Java数组 一.数组简介 数组是多个相同类型数据的组合,实现对这些数据的统一管理 数组属引用类型,数组型数据是对象(Object) 数组中的元素可以是任何数据类型,包括基本类型和引用类型 数组类型是 ...
- Docker进阶:容器卷、DockerFile、Docker网络原理
1.Docker镜像 1.1镜像是什么 镜像是一种轻量级.可执行的独立软件包,用来打包软件运行环境和机遇运行环境开发的软件. 包含一个软件的所有内容.蒋所有的应用和环境,直接打包为docker镜像,直 ...
- SESSION和JWT
1.传统登录的方式是使用 session + token,比较适用于Web应用的会话管理.token 是指在客户端使用 token 作为用户状态凭证,浏览器一般存储在 localStorage 或者 ...
- ARTS第五周
-第五周.这两周在复习大学里的课程,发现当时觉得课上很多看不懂的,现在看起来轻松多了,也带来了新的感悟. 1.Algorithm:每周至少做一个 leetcode 的算法题2.Review:阅读并点评 ...
- python 读取 查询 更新 删除 sql2008 类及应用
import pymssql class MSSQL: def __init__(self,host,user,pwd,db): self.host = host self.user = user s ...
- DNS服务器安全---通过ipset对DNS异常解析流量的源IP地址进行管控
ipset介绍 ipset是iptables的扩展,它允许你创建 匹配整个地址集合的规则.而不像普通的iptables链只能单IP匹配, ip集合存储在带索引的数据结构中,这种结构即时集合比较大也可以 ...
- java内存模型——重排序
线程安全问题概括来说表现为三个方面:原子性,可见性和有序性. 在多核处理器的环境下:编译器可能改变两个操作的先后顺序:处理器可能不是完全依照程序的目标代码所指定的顺序执行命令:一个处理器执行的多个操作 ...
- 使用adb如何批量给设备安装apk
win系统 1.首先我们需要在本地建一个文件夹apks,然后把所要安装的apk放进去 2.打开dos窗口使用for循环进行安装即可(前提你的电脑已经连接上了设备,输入adb devices可查看) f ...