splay

Nauuo and ODT CF1172E

纵然是瞬间 提交于 2019-11-27 09:22:23
一道LCT练手题, 将询问离线,单独考虑每个颜色 我们要求的就是至少经过某个颜色一次的路径数。 考虑容斥, 就是用总的路径数减去不经过的次数 标记那个颜色的点为白色,其他的为黑色 不经过的次数就是黑连通块的大小的平方。 我们将所有的黑点向父亲连边,每个黑连通块实际上最上面有一个白点,我们维护子树的平方和,每次到顶上的白点统计答案即可。 /* @Date : 2019-08-14 08:07:13 @Author : Adscn (adscn@qq.com) @Link : https://www.cnblogs.com/LLCSBlog */ #include<bits/stdc++.h> using namespace std; #define IL inline #define RG register #define gi getint() #define gc getchar() #define File(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout) IL int getint() { RG int xi=0; RG char ch=gc; bool f=0; while(ch<'0'||ch>'9')ch=='-'?f=1:f,ch=gc; while(ch>='0'&&ch<='9')xi=(xi<<1)

【洛谷P4234】最小差值生成树

♀尐吖头ヾ 提交于 2019-11-27 08:30:15
Description 给定一张n个点,m条边的无向图,求出边权最大值和最小值差值最小的生成树 Solution LCT+并查集 按照最小生成树的思路,先将边按照边权从小到大排序,然后顺序考虑每一条边 如果当前这条边的两个端点没有连通,那么直接连通 如果两个端点已经连通,我们加上这条边会形成一个环,那么为了让“边权最大值和最小值差值”尽可能小,我们可以将这个环上最短的一条边删掉,换成这条边(显然是对的) 维护最小值可以通过LCT实现,连边、短边也是LCT的基本操作 当目前的边已经是一棵树的时候更新答案即可完成 Code 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 50010; 5 const int M = 200010; 6 inline int read() { 7 int ret = 0, op = 1; 8 char c = getchar(); 9 while (!isdigit(c)) { 10 if (c == '-') op = -1; 11 c = getchar(); 12 } 13 while (isdigit(c)) { 14 ret = (ret << 3) + (ret << 1) + c - '0'; 15 c =

[Luogu] 排序机械臂

萝らか妹 提交于 2019-11-27 08:14:17
https://www.luogu.org/problemnew/solution/P3165 预处理 我们会发现一个问题:高度是无序的,而splay中要求有序,否则kth不能正确求解。 不需要求高度,只要求位置。 所以,用结构体存入 高度 与 下标,按高度排序,然后就可以把高度丢一边了(一波sao操作)。 记得头尾添加两个节点。。。 建树 正常 nlogn 可能会被卡常,所以,类似于线段树的建树,分此节点与左右儿子节点。 区间第k大 正常kth 区间翻转 首先,需要把所求的节点(即排序前下标为id的节点) splay 到 root 。 那么答案就是root左孩子的节点个数(因为有哨兵节点,所以+1-1抵消),记为s。 然后,取出 [i+1 , s+1] 这段区间,即: 将i节点 splay 到根,s+2节点 splay 到i的右节点。 再将s+2的左孩子打上翻转标记即可。 #include<iostream> #include<algorithm> #include<cstdio> #define MAXN 100010 #define MAX 999999999//最值 using namespace std; int n,size=1,root=0; struct node { int x,id; } b[MAXN]; namespace splay { struct

P2234 [HNOI2002]营业额统计

白昼怎懂夜的黑 提交于 2019-11-27 05:21:36
题目链接 平衡树练手题,我们看它这个数列是动态插入的,所以自然而然就会想到用平衡树来维护。平衡树Splay推荐大家看 这篇博客 其实差的最小值只有可能是它与其前驱或后继之差,不然就没有更小的了。因为节点是动态插入的,根据Splay的性质,我们为保证复杂度,就会每次将操作节点旋到根,而他的前驱和后继必然就是之前插入过的数。最后将最小值求和即可。 #include<cstdio> #include<algorithm> using namespace std; const int maxn=1e6+7; const int INF=0x7fffffff; int ch[maxn][2]; int n,x; int rt,sz; int cnt[maxn],fa[maxn],size[maxn],key[maxn]; int ans; void pushup(int x){ size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x]; } bool check(int x){ return ch[fa[x]][1]==x; } void rotate(int x){ int y=fa[x],z=fa[y],who=check(x); ch[y][who]=ch[x][who^1]; fa[ch[y][who]]=y; ch[x][who^1]=y; fa[y

BUG全集(我遇到的)

末鹿安然 提交于 2019-11-27 00:58:07
BUG全集 写给自己看的 每次代码容易出问题,有的时候还是重复的,想写很久了 这次来更,为了以后写题少一些bug,多一些时间想正解! dfs 首先dfs不要写傻了 有一道题比方说要选n个步骤 每个步骤有3种 是对一个数列进行不同的修改,这时候一边选步骤,一边修改,是比选完n个步骤再一次性修改要快的。 其次,如果每次要带一个参数的话,设为p,如果在void dfs里做出了 s=p,s.... dfs(s)的情况,s必须开在void里,开在全局变量会导致出错 参数不要带错了,回溯的话记得清数组和变量 有多个dfs的时候,不要写错dfs的名字了,记得区分 2、bfs bfs似乎没什么 q.front()之后记得及时pop 3、线段树 build,update,query之后记得pushup,单点修改,区间修改,询问的时候记得pushdown 如果是树上维护最大子段和之类 记得考虑要不要反过来 树剖 的时候不要update错了 记得脑补一下LCA的板子和跳链的情景 4、splay \[Splay·一生之敌\] 草每次splay的题都要写/调一天 好烦的啊啊啊啊啊 inline int kth(int rt,int k){ int x=rt; while (1){ //printf("x=%d rc=%d\n",x,rc); if (siz[lc]>=k) x=lc; else if

splay

那年仲夏 提交于 2019-11-26 23:38:35
我觉得还是有必要写一下有关splay模板解读 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删除一个) 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数) 求x的后继(后继定义为大于x,且最小的数) 输入格式 第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号( 1≤opt≤6 ) 输出格式 对于操作3,4,5,6每行输出一个数,表示对应答案 #include<bits/stdc++.h> #define re return #define inc(i,l,r) for(int i=l;i<=r;++i) const int maxn=100005; using namespace std; template<typename T>inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } int tot,n,rt,ch

@codeforces - 414E@ Mashmokh's Designed Problem

不问归期 提交于 2019-11-26 20:44:06
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一棵 n 个点的树,每个点的儿子是有序的。 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离。 (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子。 (3)给定 k,询问在当前这棵树上 dfs 后得到 dfs 序中,最后一个深度为 k 的点的编号。 Input 第一行包含两个整数 n, m (2 ≤ n ≤ 10^5; 1 ≤ m ≤ 10^5),表示点数与询问数。 接下来 n 行每行一个 li,表示 i 号结点的儿子个数。紧接着输入 li 个整数,第 j 个整数表示 i 的第 j 个儿子编号(再次强调儿子是有序的)。 接下来 m 行每行形如 "1 v u", "2 v h", 或 "3 k"。第一个数描述操作种类,接下来描述了这个操作的参数。 保证操作合法。 Output 对于 1, 3 询问,输出其对应的答案。 Examples Input1 4 9 1 2 1 3 1 4 0 1 1 4 2 4 2 1 3 4 3 1 3 2 2 3 2 1 1 2 3 1 3 2 Output1 3 2 2 4 1 3 4 Input2 2 2 1 2 0

浅谈伸展树(Splay)

做~自己de王妃 提交于 2019-11-26 20:33:47
//本文是一个暂时的小记,有不对的请大佬们指出~ 真正大佬的在这http://blog.csdn.net/clove_unique/article/details/50630280 伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由丹尼尔·斯立特Daniel Sleator和罗伯特·恩卓·塔扬Robert Endre Tarjan在1985年发明的。 在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。 它的优势在于不需要记录用于平衡树的冗余信息。 (本段来自百度) Splay的基本操作 get操作 主要用来查找x结点属于其父亲结点的左儿子还是右儿子 int get(int x) { return son[fa[x]][1]==x; } update操作 主要是更新size值 (有的时候也会更新其他) void update(int x) { if (x) { size[x]=1; if

Codeforces 482E ELCA (LCT)

一个人想着一个人 提交于 2019-11-26 19:15:29
题目链接 http://codeforces.com/contest/482/problem/E 题解 T2智商题T3大LCT题,我一个也不会= = CF的标算好像是分块?反正现在LCT都普及了就用LCT好了。 首先算期望推个式子,易得答案为 \(\sum_u a[u](sz[u]^2-\sum_{v\in son[u]} sz[v]^2)\) ( \(sz\) 为子树大小),令求和的那个东西等于 \(f[u]\) 并且如果往一个 \(u\) 里新添一个儿子 \(v\) ,增添后的子树大小是 \(sz[v]\) , 那么新增的答案是 \(a[u]sz[v](sz[u]-sz[v])\) 然后我们要支持换父亲,动态维护这个东西 后面的就是莽上一个LCT。 这个只能详见代码,解释一下代码里变量的含义 对于一个Splay结构体 \(u\) : \(ans\) : 这个点Splay的子树中所有点的 \(f\) 之和。Splay的根的 \(ans\) 就是要求的答案。 \(sz01\) : 这个点所有虚子树的大小之和加上本身的 \(1\) 。( \(sz\) 是原树中子树大小) \(sz02\) : 这个点本身所有虚子树的大小平方和,不包括本身。 \(ans0\) : 这个点本身所有虚儿子的 \(ans\) 之和。 \(sz11\) : 这个点所有实儿子和虚儿子的大小之和。 \(sum\)