题解

写数据结构的时候我代码就会变得非常非常长

一看别人1.5K 2.3K

我6.3K……

orzzzzz

我们很容易想到离线倒着插入,然而,有个小锅叫如果size相同保持原来的重儿子不变

我们需要写个线段树,遇到两个size相同的儿子时看两个儿子下一次插入是什么时候,取下一次插入时间较大的儿子,如果都没有插入,取左儿子

最后类似lct一样用splay维护每条链,但是我们不用维护虚边,要支持给splay打标记,因为我们插入一个点要给整条链增加一遍size

代码

#include <bits/stdc++.h>
#define enter putchar('\n')
#define space putchar(' ')
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define MAXN 200005
#define mo 99994711
#define pb push_back
#define eps 1e-8
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 - '0' + c;
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) out(x / 10);
putchar('0' + x % 10);
}
int N,Q;
int L[MAXN],R[MAXN],fa[MAXN],rt;
int D[MAXN],son[MAXN],siz[MAXN],dfn[MAXN],idx,Line[MAXN],tims[MAXN];
bool vis[MAXN];
int64 sum,ans[MAXN];
namespace seg_tr {
struct tr_node {
int l,r,v;
}tr[MAXN * 4];
void update(int u) {
tr[u].v = max(tr[u << 1].v,tr[u << 1 | 1].v);
}
void build(int u,int l,int r) {
tr[u].l = l;tr[u].r = r;
if(l == r) {tr[u].v = tims[Line[l]];return;}
int mid = (l + r) >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
update(u);
}
void Change(int u,int pos) {
if(tr[u].l == tr[u].r) {tr[u].v = 0;return;}
int mid = (tr[u].l + tr[u].r) >> 1;
if(pos <= mid) Change(u << 1,pos);
else Change(u << 1 | 1,pos);
update(u);
}
int Query(int u,int l,int r) {
if(l > r) return 0;
if(tr[u].l == l && tr[u].r == r) return tr[u].v;
int mid = (tr[u].l + tr[u].r) >> 1;
if(r <= mid) return Query(u << 1,l,r);
else if(l > mid) return Query(u << 1 | 1,l,r);
else return max(Query(u << 1,l,mid),Query(u << 1 | 1,mid + 1,r));
}
}
namespace splay {
struct node {
node *lc,*rc,*fa;
int siz,add;
void add_lazy(int v) {
add += v;
siz += v;
}
void push_down() {
if(lc) lc->add_lazy(add);
if(rc) rc->add_lazy(add);
add = 0;
}
}pool[MAXN],*tail = pool,*tr[MAXN];
node *que[MAXN];
int tot;
node *Newnode() {
node *res = tail++;
res->lc = res->rc = res->fa = NULL;
res->siz = 0;res->add = 0;
return res;
}
void Init(int N) {
for(int i = 1 ; i <= N ; ++i) tr[i] = Newnode();
}
void rotate(node *u) {
node *v = u->fa,*w = v->fa;
if(w) (v == w->lc ? w->lc : w->rc) = u;
node *b = (u == v->lc ? u->rc : u->lc);
if(b) b->fa = v;
v->fa = u;u->fa = w;
if(u == v->lc) {u->rc = v;v->lc = b;}
else {u->lc = v;v->rc = b;}
}
bool which(node *u) {
return u->fa->rc == u;
}
void Splay(node *u) {
tot = 0;
for(node *x = u ; x ; x = x->fa) que[++tot] = x;
for(int i = tot ; i >= 1 ; --i) que[i]->push_down();
while(u->fa) {
if(u->fa->fa) {
if(which(u) == which(u->fa)) rotate(u->fa);
else rotate(u);
}
rotate(u);
}
}
}
using seg_tr::Change;
using seg_tr::Query;
using splay::Splay;
using splay::pool;
using splay::tr;
void dfs1(int u) {
dfn[u] = ++idx;Line[idx] = u;
siz[u] = 1;
if(L[u]) {dfs1(L[u]);siz[u] += siz[L[u]];}
if(R[u]) {dfs1(R[u]);siz[u] += siz[R[u]];}
}
int dfs2(int u) {
if(vis[u] || !u) {return 0;}
int res = 1,s1,s2;
s1 = dfs2(L[u]);s2 = dfs2(R[u]);
res += s1 + s2;tr[u]->siz = res;
if(s1 == 0 && s2 == 0) return res;
if(!s1) son[u] = R[u];
else if(!s2) son[u] = L[u];
else {
if(s1 > s2) son[u] = L[u];
else if(s2 > s1) son[u] = R[u];
else {
s1 = Query(1,dfn[L[u]],dfn[L[u]] + siz[L[u]] - 1);
s2 = Query(1,dfn[R[u]],dfn[R[u]] + siz[R[u]] - 1);
if(s1 >= s2) son[u] = L[u];
else son[u] = R[u];
}
}
if(son[u]) {
Splay(tr[son[u]]);
tr[u]->rc = tr[son[u]];
tr[son[u]]->fa = tr[u];
}
return res;
}
bool check(int u,int v) {
int s1 = Query(1,dfn[u],dfn[u] + siz[u] - 1),s2 = Query(1,dfn[v],dfn[v] + siz[v] - 1);
if(s1 > s2 || (s1 == s2 && u == L[fa[u]])) return true;
return false;
}
void Insert(int u) {
while(u) {
int f = fa[u];
Splay(tr[u]);
if(!f) break;
bool flag = 0;
if(!son[f]) {flag = 1;}
else {
Splay(tr[son[f]]);
if(tr[son[f]]->siz < tr[u]->siz + 1) flag = 1;
else if(tr[son[f]]->siz == tr[u]->siz + 1) flag = check(u,son[f]);
}
if(flag) {
Splay(tr[f]);Splay(tr[u]);
if(tr[f]->rc) tr[f]->rc->fa = NULL;
tr[f]->rc = tr[u];tr[u]->fa = tr[f];
sum += u - son[f];son[f] = u;
} Splay(tr[u]);
splay::node *p = tr[u];
while(p->lc) p = p->lc;
u = p - pool + 1;
if(!flag) {
u = fa[u];
Splay(tr[u]);
p = tr[u];
while(p->lc) p = p->lc;
u = p - pool + 1;
}
}
}
void Change_size(int u) {
while(u) {
Splay(tr[u]);
if(tr[u]->lc) tr[u]->lc->add_lazy(1);
tr[u]->siz++;
splay::node *p = tr[u];
while(p->lc) p = p->lc;
u = p - pool + 1;
u = fa[u];
}
}
void Solve() {
read(N);
for(int i = 1 ; i <= N ; ++i) {read(L[i]);read(R[i]);fa[L[i]] = i;fa[R[i]] = i;}
splay::Init(N);
for(int i = 1 ; i <= N ; ++i) {if(!fa[i]) rt = i;}
read(Q);
for(int i = 1 ; i <= Q ; ++i) {read(D[i]);vis[D[i]] = 1;tims[D[i]] = i;}
dfs1(rt);
seg_tr::build(1,1,N);
dfs2(rt);
sum = 0;
for(int i = 1 ; i <= N ; ++i) sum += son[i];
ans[Q + 1] = sum;
for(int i = Q ; i >= 1 ; --i) {
Change(1,dfn[D[i]]);
Insert(D[i]);
Change_size(D[i]);
ans[i] = sum;
}
for(int i = 1 ; i <= Q + 1; ++i) {out(ans[i]);enter;}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}

【LOJ】#2079. 「JSOI2016」轻重路径的更多相关文章

  1. loj#2071. 「JSOI2016」最佳团体

    题目链接 loj#2071. 「JSOI2016」最佳团体 题解 树形dp强行01分规 代码 #include<cstdio> #include<cstring> #inclu ...

  2. loj#2076. 「JSOI2016」炸弹攻击 模拟退火

    目录 题目链接 题解 代码 题目链接 loj#2076. 「JSOI2016」炸弹攻击 题解 模拟退火 退火时,由于答案比较小,但是温度比较高 所以在算exp时最好把相差的点数乘以一个常数让选取更差的 ...

  3. [LOJ 2082] 「JSOI2016」炸弹攻击 2

    [LOJ 2082] 「JSOI2016」炸弹攻击 2 链接 链接 题解 枚举发射源,将发射源当做原点,对敌人和激光塔极角排序. 由于敌人纵坐标均为正,而其它点均为负,因此每两个角度差在 \(\pi\ ...

  4. LOJ#2082. 「JSOI2016」炸弹攻击 2(计算几何+双指针)

    题面 传送门 题解 我们枚举一下发射源,并把敌人和激光塔按极角排序,那么一组合法解就是两个极角之差不超过\(\pi\)且中间有敌人的三元组数,预处理一下前缀和然后用双指针就行了 //minamoto ...

  5. LOJ#2076. 「JSOI2016」炸弹攻击(模拟退火)

    题面 传送门 题解 退火就好了 记得因为答案比较小,但是温度比较高,所以在算\(\exp\)的时候最好把相差的点数乘上一个常数来让选取更劣解的概率降低 话虽如此然而我自己打的退火答案永远是\(0\)- ...

  6. loj#2574. 「TJOI2018」智力竞赛 (路径覆盖)

    目录 题目链接 题解 代码 题目链接 loj#2574. 「TJOI2018」智力竞赛 题解 就是求可重路径覆盖之后最大化剩余点的最小权值 二分答案后就是一个可重复路径覆盖 处理出可达点做二分图匹配就 ...

  7. Loj #3057. 「HNOI2019」校园旅行

    Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...

  8. LOJ #2359. 「NOIP2016」天天爱跑步(倍增+线段树合并)

    题意 LOJ #2359. 「NOIP2016」天天爱跑步 题解 考虑把一个玩家的路径 \((x, y)\) 拆成两条,一条是 \(x\) 到 \(lca\) ( \(x, y\) 最近公共祖先) 的 ...

  9. loj#2013. 「SCOI2016」幸运数字 点分治/线性基

    题目链接 loj#2013. 「SCOI2016」幸运数字 题解 和树上路径有管...点分治吧 把询问挂到点上 求出重心后,求出重心到每个点路径上的数的线性基 对于重心为lca的合并寻味,否则标记下传 ...

随机推荐

  1. 【题解】 bzoj2115: [Wc2011] Xor (线性基+dfs)

    bzoj2115,戳我戳我 Solution: 看得题解(逃,我太菜了,想不出这种做法 那么丢个链接 Attention: 板子别写错了 又写错了这次 \(long long\)是左移63位,多了会溢 ...

  2. 自学huawei之路-AC6005-8AP添加授权码

    返回自学Huawei之路 自学huawei之路-AC6005-8AP添加授权码

  3. 【BZOJ3745】Norma(CDQ分治)

    [BZOJ3745]Norma(CDQ分治) 题面 BZOJ 洛谷 题解 这种问题直接做不好做,显然需要一定的优化.考虑\(CDQ\)分治. 现在唯一需要考虑的就是跨越当前中间节点的所有区间如何计算答 ...

  4. 【bzoj3224】 Tyvj1728—普通平衡树

    http://www.lydsy.com/JudgeOnline/problem.php?id=3224 (题目链接) 题意 1. 插入x数:2. 删除x数(若有多个相同的数,因只删除一个):3. 查 ...

  5. SpringBoot整合Mybatis之xml

    SpringBoot整合Mybatis mybatis ORM框架.几个重要的概念: Mapper配置 : 可以使用基于XML的Mapper配置文件来实现,也可以使用基于Java注解的Mybatis注 ...

  6. 个推Node.js 微服务实践:基于容器的一站式命令行工具链

    作者:个推Node.js 开发工程师 之诺 背景与摘要 由于工程数量的快速增长,个推在实践基于 Node.js 的微服务开发的过程中,遇到了如下问题: 1. 每次新建项目都需要安装一次依赖,这些依赖之 ...

  7. saltstack主机管理项目【day39】:主机管理项目开发

    项目目标 salt state.apply -h "ubuntu,centos" -g "ubuntu,centos" -f "ubuntu,cent ...

  8. openstack项目【day23】:openstack组件介绍

    本节内容 openstack介绍 openstack项目(服务名是项目名的别名) openstack运行流程 openstack各组件详解 一:openstack介绍             open ...

  9. mybatis+sqlserver中返回非自增主键

    首先把实体类贴出来(这里只贴出属性,其它的就是getter和setter方法): public class Around {     private String xccd;  //对应主键      ...

  10. alloffthelights使用方法

    官网上的demo没有用,因为官网用的jquery是谷歌的CDN,download下来的demo也没有用,因为demo的路径下少了jquery.所以自己写demo的时候要把jquery和alloffth ...