题目链接

很好的一道题,用了三天多的时间,终于知道了我为什么T的原因,也知道了在Splay的同时该怎样子的节约时间,因为Splay本身就是大常数的O(N*logN),我们如果不在各种细节上节约时间,很容易就会造成T的是因为我们制造了一个同样的大常数。

先讲解一下题意:有两种操作,一个是一段区间内的值翻转,另一个是讲[a, b]裁剪下来,然后将这段区间贴到剩下的元素的第c位后面。

然后讲一下Splay翻转的几个要点,还有就是可能的T的点,我们要做到的翻转的操作自然不是太过于难想,很多人都能想到的就是lazy标记的下推,然后改变相对应的值即可。同样的,区间的裁剪再贴到最后也不是那么的难想到,就是裁剪[l, r]这样的区间,我们只需要找到Kth(l-1)和Kth(r+1),然后裁剪下来他们的中间区域,(这里记得要先pushup(r+1),然后pushup(l-1)也就是根节点)然后再去寻找到第c大的,还有c+1大的,我们将值放在(c+1)大的左子树那块即可。思路就是这样子的,但是代码总是T,我一开始还写了快速读入,还以为能卡时间,但是很显然,时间不在于输入处,而是Splay的时候,翻转过于浪费时间,那么具体是哪里浪费时间了呢,我们慢慢看。

现在开始讲解Splay会T的一些原因,Splay的操作主要就是在Splay()这个函数,所以当T的时候,我们应该先考虑一下是不是在翻转Splay()的时候有一些可以去掉的大常数,譬如说,我将pushup()还有pushdown()这两个函数写在了Rotate()函数里面,那么我们就不必要再在Splay()函数里面写pushup()和pushdown()了,这里就是可以优化掉一个大常数(这是我到最后发现的TLE的最最最关键的原因)。

当然,我们裁剪掉一部分pushup()以及pushdown()的同时,还需要保证留下足够的对应的返回更新和下放懒标记的函数才行,这里给出哪里必需要放置这两个函数:

inline void pushup(int x) { t[x].siz = t[t[x].ch[]].siz + t[t[x].ch[]].siz + ; }

pushup

inline void pushdown(int x)
{
if(t[x].lazy)
{
t[t[x].ch[]].lazy ^= ;
t[t[x].ch[]].lazy ^= ;
swap(t[x].ch[], t[x].ch[]);
t[x].lazy = ;
}
}

pushdown

然后接下去说说看哪几处地方一定是要放这两个函数的:

void Rotate(int x)
{
int y = t[x].ff, z = t[y].ff;
pushdown(y); pushdown(x);
int k = t[y].ch[] == x;
t[z].ch[t[z].ch[] == y] = x;
t[x].ff = z;
t[y].ch[k] = t[x].ch[k^];
t[t[x].ch[k^]].ff = y;
t[x].ch[k^] = y;
t[y].ff = x;
pushup(y); pushup(x);
}

Rotate

在Rotate的时候是一定要加上这两个函数的,因为我们在旋转的同时我们会改变节点,所以首先就需要pushdown(),然后,最后需要pushup()。

inline void build_Splay_tree(int &rt, int l, int r, int fa)
{
if(l > r) return;
int mid = HalF;
new_node(rt, fa, mid);
build_Splay_tree(t[rt].ch[], l, mid-, rt);
build_Splay_tree(t[rt].ch[], mid + , r, rt);
pushup(rt);
}

build_Splay_tree

在建Splay树的时候,我们就需要pushup()来得到size的传递。

int Kth(int k)
{
int u = root;
if(t[u].siz < k) return ;
while(true)
{
pushdown(u);
int y = t[u].ch[];
if(k > t[y].siz + )
{
k -= t[y].siz + ;
u = t[u].ch[];
}
else
{
if(t[y].siz >= k) u = y;
else return u; //找到对应的节点编号了
}
}
}

Kth

我们要找第K个数的时候,也需要pushdown(),因为我们的左右子树的size是不一定相同的,我们要改变翻转的懒标记,然后同时改变左右的子树的位置。

inline void Cut(int l, int r, int pos)
{
int las = Kth(l), nex = Kth(r + );
Splay(las, ); Splay(nex, las);
int del = t[nex].ch[];
t[nex].ch[] = ;
pushup(nex);
pushup(root);
int u = Kth(pos + ), v = Kth(pos + );
Splay(u, ); Splay(v, u);
t[t[root].ch[]].ch[] = del;
t[del].ff = v;
pushup(v);
pushup(u);
}

Cut

我们cut下来的时候,就需要一次的pushup(),因为此时的两个节点的size都改变了,然后在接下去,我们接上去的时候也需要pushup(),这时候这两个节点就有更多的size了,也是需要更新了。

inline void _OUT(int u)
{
pushdown(u);
if(t[u].ch[]) _OUT(t[u].ch[]);
if(t[u].val >= && t[u].val <= N) { cnt++; printf("%d%c", t[u].val, cnt == N ? '\n' : ' '); }
if(t[u].ch[]) _OUT(t[u].ch[]);
}

OUT

输出的操作当然是要pushdown()的呀,毕竟要对应的找左右嘛,所谓的中序遍历。


好了,上面的所有的情况都考虑到了,基本就能A了吧,也祝愿大家都能过美美哒过这题,不像我……卡了好几天…… (*≧ω≦)

FLIP
CUT
FLIP
CUT
ans:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 3e5 + ;
int N, M, root, tot;
struct node
{
int ff, val, siz, ch[], lazy;
node() { ff = val = siz = ch[] = ch[] = lazy = ; }
}t[maxN];
inline void new_node(int &x, int fa, int val)
{
x = ++tot;
t[x].ff = fa;
t[x].val = val;
t[x].siz = ;
t[x].ch[] = t[x].ch[] = t[x].lazy = ;
}
inline void pushdown(int x)
{
if(t[x].lazy)
{
t[t[x].ch[]].lazy ^= ;
t[t[x].ch[]].lazy ^= ;
swap(t[x].ch[], t[x].ch[]);
t[x].lazy = ;
}
}
inline void pushup(int x) { t[x].siz = t[t[x].ch[]].siz + t[t[x].ch[]].siz + ; }
void Rotate(int x)
{
int y = t[x].ff, z = t[y].ff;
pushdown(y); pushdown(x);
int k = t[y].ch[] == x;
t[z].ch[t[z].ch[] == y] = x;
t[x].ff = z;
t[y].ch[k] = t[x].ch[k^];
t[t[x].ch[k^]].ff = y;
t[x].ch[k^] = y;
t[y].ff = x;
pushup(y); pushup(x);
}
void Splay(int x, int goal)
{
while(t[x].ff != goal)
{
int y = t[x].ff, z = t[y].ff;
if(z != goal) (t[z].ch[] == y) ^ (t[y].ch[] == x) ? Rotate(x) : Rotate(y);
Rotate(x);
}
if(!goal) root = x;
}
inline void insert(int x)
{
int u = root, ff = ;
while(u && t[u].val != x)
{
ff = u;
u = t[u].ch[x > t[u].val];
}
new_node(u, ff, x);
if(ff) t[ff].ch[x > t[ff].val] = u;
Splay(u, );
}
inline void build_Splay_tree(int &rt, int l, int r, int fa)
{
if(l > r) return;
int mid = HalF;
new_node(rt, fa, mid);
build_Splay_tree(t[rt].ch[], l, mid-, rt);
build_Splay_tree(t[rt].ch[], mid + , r, rt);
pushup(rt);
}
int Kth(int k)
{
int u = root;
if(t[u].siz < k) return ;
while(true)
{
pushdown(u);
int y = t[u].ch[];
if(k > t[y].siz + )
{
k -= t[y].siz + ;
u = t[u].ch[];
}
else
{
if(t[y].siz >= k) u = y;
else return u; //找到对应的节点编号了
}
}
}
inline void Ex_change(int l, int r)
{
int las = Kth(l), nex = Kth(r + );
Splay(las, ); Splay(nex, las);
t[t[nex].ch[]].lazy ^= ;
}
inline void Cut(int l, int r, int pos)
{
int las = Kth(l), nex = Kth(r + );
Splay(las, ); Splay(nex, las);
int del = t[nex].ch[];
t[nex].ch[] = ;
pushup(nex);
pushup(root);
int u = Kth(pos + ), v = Kth(pos + );
Splay(u, ); Splay(v, u);
t[t[root].ch[]].ch[] = del;
t[del].ff = v;
pushup(v);
pushup(u);
}
int cnt;
inline void _OUT(int u)
{
pushdown(u);
if(t[u].ch[]) _OUT(t[u].ch[]);
if(t[u].val >= && t[u].val <= N) { cnt++; printf("%d%c", t[u].val, cnt == N ? '\n' : ' '); }
if(t[u].ch[]) _OUT(t[u].ch[]);
}
inline void init()
{
tot = root = cnt = ;
t[].siz = t[].ch[] = t[].ch[] = ;
build_Splay_tree(root, , N + , );
}
char op[];
int main()
{
while(scanf("%d%d", &N, &M) != EOF)
{
if(N < || M < ) break;
init();
int a, b, c;
while(M--)
{
scanf("%s", op);
if(op[] == 'C')
{
scanf("%d%d%d", &a, &b, &c);
Cut(a, b, c);
}
else
{
scanf("%d%d", &a, &b);
Ex_change(a, b);
}
//cnt = 0;
//_OUT(root);
}
_OUT(root);
}
return ;
}
/*
5 4
FLIP 2 4
CUT 2 3 3
FLIP 2 5
CUT 1 3 1
ans:5 1 3 4 2
*/

完整代码

Play with Chain 【HDU - 3487】【Splay+TLE讲解】的更多相关文章

  1. HDU 3487 Splay tree

    Play with Chain Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  2. HDU 3487 Splay

    给定两种操作,一种是把一个数列的某一段切下来插到剩余数列的某一个位置上. 一种是翻转操作,把数列的某一段进行翻转. 都是Splay的基本操作.标准的Rotateto调整出 [a,b]区间.然后对[a, ...

  3. HDU 3487 Play with Chain(Splay)

    题目大意 给一个数列,初始时为 1, 2, 3, ..., n,现在有两种共 m 个操作 操作1. CUT a b c 表示把数列中第 a 个到第 b 个从原数列中删除得到一个新数列,并将它添加到新数 ...

  4. hdu 3487 Play with Chain

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3487 YaoYao is fond of playing his chains. He has a c ...

  5. hdu 3436 splay树+离散化*

    Queue-jumpers Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To ...

  6. hdu 4453 splay

    Looploop Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  7. HDU 3487:Play with Chain(Splay)

    http://acm.hdu.edu.cn/showproblem.php?pid=3487 题意:有两种操作:1.Flip l r ,把 l 到 r 这段区间 reverse.2.Cut a b c ...

  8. HDU 3487 Play with Chain | Splay

    Play with Chain Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  9. HDU 3487 Play with Chain (splay tree)

    Play with Chain Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

随机推荐

  1. AcWing 802. 区间和

    (https://www.acwing.com/problem/content/804/) 假定有一个无限长的数轴,数轴上每个坐标上的数都是0. 现在,我们首先进行 n 次操作,每次操作将某一位置x上 ...

  2. 小白学Python(16)——pyecharts 绘制地理图表 Geo

    Geo-基本示例 from example.commons import Faker from pyecharts import options as opts from pyecharts.char ...

  3. python 模块调用的几种方式

    在python里面又很多模块,或者引用第三方模块,python 模块调用的几种方式,下面详细解说 1,import 模块名 2,from 模块 import  模块里面的小功能 3,from  模块 ...

  4. socket服务器

    Python 3.x,已经改名为socketserver:Python 2. #coding=utf-8 #1.必须自己创建一个请求处理类,并且这个类要继承BaseRequesHandler,并且还要 ...

  5. 解决代码加载慢,以至于乱码—————VUE

    vue.js解决开始代码加载,以至于乱码 vue.js通过几行代码可以解决这个问题 css: [v-cloak] { display: none; } html: <div id="a ...

  6. APP性能

    一.APP性能维度分析  APP类型众多,根据具体类型划分,性能指标的维度和优先级各不相同.视频类APP归属于娱乐游戏型的APP,因此性能测试维度优先级排序为:流畅度.crash.内存.流量.响应时长 ...

  7. SwiftUI 实战:从 0 到 1 研发一个 App

    心得感悟 起初看到 WWDC 上的演示 SwiftUI 时,我就觉得 SwiftUI 有种陌生的熟悉感(声明式语法),所以体验下,看看有没有什么启发. 先说下整体项目完成下来的感受: 用 Swift ...

  8. 读取FTP上的excel文件,并写入数据库

    今天遇到一些问题,需要从ftp上读取一些excel文件,并需要将excel中的数据写入到数据库,这样就可以通过管理页面查看这些数据. 我将相关工作分为三步,1.从ftp上读取相关文件,并将excel文 ...

  9. PyQt5+qtdesigner开发环境配置

    1.PyQt5安装 pip install PyQt5 2.qtdesigner安装 本来直接用pip install PyQt5-tools安装的,但是网速下的慢,中间还断了几次,在网上找到一个稳定 ...

  10. 解决pycharm运行py文件时只有unittest选项的方法

    有时候在编完脚本开始运行时,发现某个py脚本右键运行的选项不是run,二是run in unittest,试过很多方法都不能很好的去除,主要是因为脚本中含有test字符串,一种解决方法是将脚本中所有的 ...