这题难道不是spaly裸题吗?

言归正传QWQ

一看到这个题目,其实第一反应是很懵X的

从来没有见过类似的题目啊,什么\(spaly\),单旋。QWQ很懵逼啊

不过,我们可以注意到这么一件事情,就是我们对于树中元素移动的时候,只会移动\(min或者max\)。

那么会不会有什么性质呢

QWQ

经过手玩,以\(max\)为栗,我们可以发现我们将这个点单旋到根的话,相当于就是说保持的原树的形态不变,把\(max\)的左儿子连到\(max\)的父亲,然后删除这个点,然后把\(root\)接到\(max\)的左儿子上。

最小值和最大值同理

这不就是一个\(link\)和一个\(cut\)吗QWQ

所以直接可以上\(LCT\)

每次代价,就是从当前点到根的距离

我们现在考虑怎么插入

有一个结论是,插入的时候一定会插到前驱和后继中深度比较大的那个的对应儿子。

因为因为前驱和后继一定是父子关系,只有深的那个才可能出现合法位置的空儿子

QWQ另外的话就是一些细节了

需要除了\(LCT\)之外,再维护原树的形态和\(fa\)的两个数组

然后实时维护一个\(root\),表示原树的根。每次操作完都\(makeroot\),便于计算路径长度

剩下的还是直接去看代码吧

QWQ

感觉这个题很好啊,思维挺不错的

细节也有不少

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline 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;
}
const int maxn = 3e5+1e2;
struct Node
{
int opt,val;
};
Node a[maxn];
int ch[maxn][3];//LCT中的父子关系
int fa[maxn];
int zuzong[maxn];//spaly中的父子关系
int son[maxn][3];
int n,m;
int rev[maxn],st[maxn],size[maxn];
set<int> s;
int b[maxn];
int cnt;
int root;
int sson(int x)
{
if (ch[fa[x]][0]==x) return 0;
else return 1;
}
bool notroot(int x)
{
return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void update(int x)
{
if (!x) return;
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void reverse(int x)
{
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
void pushdown(int x)
{
if(rev[x])
{
if (ch[x][0]) reverse(ch[x][0]);
if (ch[x][1]) reverse(ch[x][1]);
rev[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y];
int b=sson(x),c=sson(y);
if (notroot(y)) ch[z][c]=x;
fa[x]=z;
ch[y][b]=ch[x][!b];
fa[ch[x][!b]]=y;
ch[x][!b]=y;
fa[y]=x;
update(y);
update(x);
}
void splay(int x)
{
int y=x,cnt=0;
st[++cnt]=y;
while (notroot(y)) y=fa[y],st[++cnt]=y;
while (cnt) pushdown(st[cnt--]);
while (notroot(x))
{
int y=fa[x],z=fa[y];
int b=sson(x),c=sson(y);
if (notroot(y))
{
if(b==c) rotate(y);
else rotate(x);
}
rotate(x);
//cout<<x<<endl;
}
update(x);
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x);
ch[x][1]=y;
update(x);
}
}
void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
int findroot(int x)
{
access(x);
splay(x);
while (ch[x][0])
{
pushdown(x);
x=ch[x][0];
}
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
if (!x || !y) return;
makeroot(x);
if (findroot(y)!=x)
fa[x]=y;
}
void cut(int x,int y)
{
if (!x || !y) return;
split(x,y);
if (ch[x][0] || ch[x][1] || fa[x]!=y || ch[y][1]) return;
fa[x]=ch[y][0]=0;
update(y);
}
int query(int x)
{
access(x);
splay(x);
return size[x];
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
a[i].opt=read();
if (a[i].opt==1) a[i].val=read(),b[++cnt]=a[i].val;
}
sort(b+1,b+1+cnt);
for (int i=1;i<=n;i++)
if(a[i].opt==1) a[i].val=lower_bound(b+1,b+1+cnt,a[i].val)-b; //离散化,权值既是编号
for (int i=1;i<=n;i++)
{
if (a[i].opt==1)
{
int lyf,ymh=0;
if (s.size()==0)
{
cout<<1<<"\n";
s.insert(a[i].val);
root=a[i].val;
continue;
}
set<int> :: iterator now = s.upper_bound(a[i].val);
if(now!=s.end())
{
//ymh=max(ymh,query(*now));
if (query(*now)>=ymh) ymh=query(*now),lyf=*now;
}
if(now!=s.begin())
{
--now;
if (query(*now)>=ymh) ymh=query(*now),lyf=*now;
}
//插入的时候,应该找到前驱和后继深度较深的那个,然后插入
//因为前驱和后继一定是父子关系,只有深的那个 才可能出现合法位置的空儿子
cout<<ymh+1<<"\n";
zuzong[a[i].val]=lyf;
son[lyf][lyf<a[i].val]=a[i].val;
s.insert(a[i].val);
link(a[i].val,lyf);
}
if (a[i].opt==2)
{
int now = *(s.begin());
int faa = zuzong[now];
int ss = son[now][1];
cout<<query(now)<<"\n";
if (now==root) continue;
cut(now,faa);
cut(now,ss);
link(ss,faa);
link(root,now);
zuzong[root]=now;
zuzong[now]=0;
son[now][1]=root;
zuzong[ss]=faa;
son[faa][0]=ss;
root=now;
//找到最小值,然后手动修改原树的父子关系,然后暴力link和cut
}
if (a[i].opt==3)
{
int now = *(s.rbegin());
int faa = zuzong[now];
int ss = son[now][0];
cout<<query(now)<<"\n";
if (now==root) continue;
cut(now,faa);
cut(now,ss);
link(ss,faa);
link(root,now);
zuzong[root]=now;
zuzong[now]=0;
son[now][0]=root;
zuzong[ss]=faa;
son[faa][1]=ss;
root=now;
//和最小值同理
}
if(a[i].opt==4)
{
set<int> :: iterator pos = s.begin();
int now = *(s.begin());
int faa = zuzong[now];
int ss = son[now][1];
cout<<query(now)<<"\n";
cut(now,faa);
cut(now,ss);
link(ss,faa);
zuzong[ss]=faa;
son[faa][0]=ss;
son[now][0]=son[now][1]=zuzong[now]=0;
s.erase(now);
if (root==now) root=ss;
}
if (a[i].opt==5)
{
int now = *(s.rbegin());
int faa = zuzong[now];
int ss = son[now][0];
cout<<query(now)<<"\n";
cut(now,faa);
cut(now,ss);
link(ss,faa);
zuzong[ss]=faa;
son[faa][1]=ss;
son[now][0]=son[now][1]=zuzong[now]=0;
s.erase(now);
if (root==now) root=ss;
}
makeroot(root);
}
return 0;
}

洛谷3721 HNOI2017单旋(LCT+set+思维)的更多相关文章

  1. bzoj 4825: [Hnoi2017]单旋 [lct]

    4825: [Hnoi2017]单旋 题意:有趣的spaly hnoi2017刚出来我就去做,当时这题作死用了ett,调了5节课没做出来然后发现好像直接用lct就行了然后弃掉了... md用lct不知 ...

  2. 【LG3721】[HNOI2017]单旋

    [LG3721][HNOI2017]单旋 题面 洛谷 题解 20pts 直接模拟\(spaly\)的过程即可. 100pts 可以发现单旋最大.最小值到根,手玩是有显然规律的,发现只需要几次\(lin ...

  3. 4825: [Hnoi2017]单旋

    4825: [Hnoi2017]单旋 链接 分析: 以后采取更保险的方式写代码!!!81行本来以为不特判也可以,然后就总是比答案大1,甚至出现负数,调啊调啊调啊调~~~ 只会旋转最大值和最小值,以最小 ...

  4. [BZOJ4825][HNOI2017]单旋(线段树+Splay)

    4825: [Hnoi2017]单旋 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 667  Solved: 342[Submit][Status][ ...

  5. 【BZOJ4825】[Hnoi2017]单旋 线段树+set

    [BZOJ4825][Hnoi2017]单旋 Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能 ...

  6. 洛谷 P3721 - [AH2017/HNOI2017]单旋(LCT)

    洛谷题面传送门 终于调出来这道题了,写篇题解( 首先碰到这样的题我们肯定要考虑每种操作会对树的形态产生怎样的影响: 插入操作:对于 BST 有一个性质是,当你插入一个节点时,其在 BST 上的父亲肯定 ...

  7. bzoj P4825 [Hnoi2017]单旋——solution

    Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据 结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的 ...

  8. HNOI2017 单旋

    题目描述 网址:https://www.luogu.org/problemnew/show/3721 大意: 有一颗单旋Splay(Spaly),以key值为优先度,总共有5个操作. [1] 插入一个 ...

  9. HNOI2017单旋

    单旋 这道题做法贼多,LCT,splay,线段树什么的貌似都行. 像我这种渣渣只会线段树了(高级数据结构学了也不会用). 首先离线所有操作,因为不会有两个点值重复,所以直接离散. 一颗线段树来维护所有 ...

随机推荐

  1. 虚拟机VMWare开机黑屏 无法进入系统

    参考了: https://blog.csdn.net/x534119219/article/details/79497264 可能方案一: 关闭VMware Workstation加速3D图形设置 可 ...

  2. Vulnhub靶机渗透 -- DC5

    信息收集 通过nmap搜索到IP为:192.168.200.11 开启了80http.111RPC服务端口 先打开网页,然后进行目录爆破 contact.php 攻击 经搜索没有发现可以攻击wheel ...

  3. MMM双主-双从读写分离部署

    原文转自:https://www.cnblogs.com/itzgr/p/10233932.html作者:木二 目录 一 前期规划 1.1 主机规划 1.2 虚拟IP规划 1.3 用户列表 1.4 整 ...

  4. Mybatis笔记(2)

    一.Mybatis的Dao层实现 1.1 代理开发方式介绍 Mapper 接口开发需要遵循以下规范: 1. Mapper.xml文件中的namespace与mapper接口的全限定名相同 2. Map ...

  5. Git使用教程三

    2.远程仓库 线上仓库的操作学习以Github为例 2.1完成线上仓库创建 注意:仓库要求在当前账号下唯一   2.2 两种常规使用方式 2.2.1基于http协议 a.创建空目录,名称就称为shop ...

  6. eclipse建立c语言工程以及成功下载到FPGA芯片过程遇到的各种问题以及解决方法详解

    推荐大家预先建立好一个工程目录文件夹,确实挺好用,参考正点原子的pdf教程,如下图所示, 我们eclipse在software文件夹建立一个workspace即可 选择用helloworld模板建立工 ...

  7. 一、Git分布式版本控制系统

    1.引入 在开发一个软件项目时,本地只有几十行代码或几百行代码时还可以维护,但当代码达到一定的数量后或两三个人共同开发一个项目时,就很容易会出现代码混乱.冲突.排错难等问题.一旦开发完工以后发现整个项 ...

  8. Springboot 整合通用mapper和pagehelper展示分页数据(附github源码)

    简介 springboot 设计目的就是为了加速开发,减少xml的配置.如果你不想写配置文件只需要在配置文件添加相对应的配置就能快速的启动的程序. 通用mapp 通用mapper只支持对单表的操作,对 ...

  9. Linux环境搭建及项目部署

    一. VMWare安装图解 1.点击下一步 2.接受条款,下一步 3.选择安装目录,不建议有中文目录和空格目录.下一步 4.下一步 5.这两个选项根据可以爱好习惯选择,下一步 6.安装 7.完成 9. ...

  10. NOIP模拟16:「Star Way To Heaven·God Knows·Loost My Music」

    T1:Star Way To Heaven 基本思路:   最小生成树.   假如我们将上边界与下边界看作一个点,然后从上边界经过星星向下边界连边,会发现,他会形成一条线将整个矩形分为左右两个部分. ...