CF1208 Red Blue Tree
问题分析
这是蒟蒻第一道3500!不过话说luogu上两个题解的程序都是假的可还行(2019.11.1)……
为了方便叙述,下面我们约定 :
\([c]\) 的值为 \(1\) 当且仅当 \(c\) 为真,反之为 \(0\) 。
\(0\) 表示白色, \(1\) 表示黑色。
\(son_u\) 表示 \(u\) 的所有儿子, \(ls_u\) 表示 \(u\) 的所有轻儿子, \(hs_u\) 表示 \(u\) 的重儿子。 \(m\) 表示 \(u\) 的子节点个数。
那么实际上对于一个特定的 \(k\) ,每个点的颜色 \(C\) 都可以被这样唯一确定:
\]
也可以写成这样子:
\]
容易发现当 \(k\) 很小时,\(C\) 一定是 \(1\) ; \(k\) 很大时, \(C\) 一定是 \(0\) 。进一步分析发现,对于每一个点 \(u\) ,总有一个 \(k'\) 满足 \(C_u=0(k\geqslant k')\) 并且 \(C_u=1(k<k')\) 。而如果我们能维护每一个节点 \(u\) 的 \(k'\) (不妨记为 \(A_u\) ),就能简单地处理询问。
考虑 \(A_u\) 的性质:
C_u&=[A_u>k]\\
A_u&=\min\{k|m-2[A_{son_u}\leqslant k]-k<0\}
\end{aligned}
\]
由于每次只会改变一个叶子的颜色,那么每个 \(A\) 的值最多变化2。那么可以对每一个 \(A_u\) 的儿子,可以建一个 Binary Search Tree 来解决。每一次改变暴力往前后扫 \(k\) 变了多少。时间复杂度 \(O(n^2)\) 。当然,如果是用脚造的数据,树高很矮的话,就可以过了
(貌似变烦而且变慢了?)
其实到这里应该已经发现是动态DP了。。。
实际上这个做法花了大量的时间转移每一个父亲,那么就可以考虑用动态DP的黑科技来转移:
\]
其中前面一部分仍然是在 Binary Search Tree 上解决,后面一部分我们需要去掉这个 \([]\) 运算。(感觉思维难点就在这里。。。)
令 \(T_0\) 表示当前这条链顶为白色,且链底的重儿子为白色所需最小的 \(k\) ; \(T_1\) 表示当前这条链顶为白色,且链底的重儿子为黑色所需要最小的 \(k\) 。
这样一来,重链上的转移就满足结合律了:
现在需要从链 \(B\) 转移到链 \(A\) ,其中 \(B\) 链顶的父亲是 \(A\) 链底。那么有
\[T_0 = \min\{\max\{A_0, B_0\}, A_1\}\\
T_1 = \min\{\max\{A_0, B_1\}, A_1\}
\]而最后的答案就是 \([T_0>k]\) 。(考虑一下,一定有 \(T_0\leqslant T_1\) 。)
参考程序
代码很短的,很好写的,也就几个钟头就写完了。拖个模板,写的丑一点,不删调试信息也就十几k而已
#include <cstdio>
#include <algorithm>
const int INF = 1e7 + 10;
struct node {
int Value, Priority, Size, Count;
node *LeftChild, *RightChild;
node() {
Value = Priority = Size = Count = 0;
LeftChild = RightChild = NULL;
return;
}
node(int _Value) {
Value = _Value;
Priority = rand();
Size = Count = 1;
LeftChild = RightChild = NULL;
return;
}
inline void Update();
};
struct noneRotateTreap {
node *Root;
noneRotateTreap() {
Root = NULL;
return;
}
inline std::pair<node *, node *> Split(node *Rt, int x);
inline node *Merge(node *x, node *y);
inline node *Find(int x);
inline void Update(int x, int State);
inline void Insert(int x);
inline int Delete(int x);
inline int Rank(int x);
inline int Query(int x);
inline int Precursor(int x);
inline int Successor(int x);
void Dfs(node *Rt);
void Debug();
};
inline void node::Update() {
Size = Count;
if (LeftChild != NULL)
Size += LeftChild->Size;
if (RightChild != NULL)
Size += RightChild->Size;
return;
}
inline std::pair<node *, node *> noneRotateTreap::Split(node *Rt, int x) {
if (Rt == NULL)
return std::pair<node *, node *>(NULL, NULL);
if (x < Rt->Value) {
std::pair<node *, node *> Temp = Split(Rt->LeftChild, x);
Rt->LeftChild = Temp.second;
Rt->Update();
return std::pair<node *, node *>(Temp.first, Rt);
} else {
std::pair<node *, node *> Temp = Split(Rt->RightChild, x);
Rt->RightChild = Temp.first;
Rt->Update();
return std::pair<node *, node *>(Rt, Temp.second);
}
}
inline node *noneRotateTreap::Merge(node *l, node *r) {
if (l == NULL) return r;
if (r == NULL) return l;
if (l->Priority <= r->Priority) {
l->RightChild = Merge(l->RightChild, r);
l->Update();
return l;
} else {
r->LeftChild = Merge(l, r->LeftChild);
r->Update();
return r;
}
}
inline node *noneRotateTreap::Find(int x) {
node *Rt = Root;
while (Rt) {
if (Rt->Value == x)
return Rt;
if (x < Rt->Value)
Rt = Rt->LeftChild;
else
Rt = Rt->RightChild;
}
return NULL;
}
inline void noneRotateTreap::Update(int x, int State) {
node *Rt = Root;
while (Rt) {
Rt->Size += State;
if (Rt->Value == x) {
Rt->Count += State;
return;
}
if (x < Rt->Value)
Rt = Rt->LeftChild;
else
Rt = Rt->RightChild;
}
return;
}
inline void noneRotateTreap::Insert(int x) {
node *T = Find(x);
if (T != NULL) {
Update(x, 1);
return;
}
std::pair<node *, node *> Temp = Split(Root, x);
Temp.first = Merge(Temp.first, new node(x));
Root = Merge(Temp.first, Temp.second);
return;
}
inline int noneRotateTreap::Delete(int x) {
node *T = Find(x);
if (T == NULL) return 1;
if (T->Count > 1) {
Update(x, -1);
return 0;
}
std::pair<node *, node *> Temp1 = Split(Root, x - 1);
std::pair<node *, node *> Temp2 = Split(Temp1.second, x);
delete Temp2.first;
Root = Merge(Temp1.first, Temp2.second);
return 0;
}
#define LCS (Rt->LeftChild ? Rt->LeftChild->Size : 0)
inline int noneRotateTreap::Rank(int x) {
node *Rt = Root;
int Ans = 0;
while (Rt) {
if (Rt->Value == x)
return Ans + LCS + 1;
if (x < Rt->Value)
Rt = Rt->LeftChild;
else
Ans += LCS + Rt->Count, Rt = Rt->RightChild;
}
return Ans + 1;
}
inline int noneRotateTreap::Query(int x) {
node *Rt = Root;
while (Rt) {
if (LCS < x && x <= LCS + Rt->Count)
return Rt->Value;
if (x <= LCS)
Rt = Rt->LeftChild;
else
x -= LCS + Rt->Count, Rt = Rt->RightChild;
}
return 0;
}
#undef LCS
inline int noneRotateTreap::Precursor(int x) {
int Ans = INF;
node *Rt = Root;
while (Rt) {
if (Rt->Value < x)
Ans = Rt->Value, Rt = Rt->RightChild;
else
Rt = Rt->LeftChild;
}
return Ans;
}
inline int noneRotateTreap::Successor(int x) {
int Ans = -INF;
node *Rt = Root;
while (Rt) {
if (Rt->Value > x)
Ans = Rt->Value, Rt = Rt->LeftChild;
else
Rt = Rt->RightChild;
}
return Ans;
}
void noneRotateTreap::Debug() {
Dfs(Root);
printf("\n");
return;
}
void noneRotateTreap::Dfs(node *T) {
if (T == NULL) return;
Dfs(T->LeftChild);
for (int i = 1; i <= T->Count; ++i)
printf("%d ", T->Value);
Dfs(T->RightChild);
return;
}
/*========== HEAD TEMPLATE : Binaty Search Tree -> NoneRotateTreap ==========*/
//#define DEBUG
//#define DEBUG_
//#define _DEBUG_
const int Maxn = 200010;
struct edge {
int To, Next;
edge() {}
edge(int _To, int _Next) : To(_To), Next(_Next) {}
};
edge Edge[Maxn << 1];
int Start[Maxn], Used;
int Num, N, Q, Type, C[Maxn], k;
noneRotateTreap NoneRotateTreap[Maxn];
int CountSon[Maxn];
int Size[Maxn], Father[Maxn], Deep[Maxn], Son[Maxn], Top[Maxn], Dfn[Maxn], Ref[Maxn], Bottom[Maxn];
std::pair<int, int> IntervalTree[Maxn << 2];
int TreeSize;
int Record[Maxn];
inline void AddEdge(int x, int y);
inline void CutDfs1(int u, int Fa);
inline void CutDfs2(int u, int Fa);
inline std::pair<int, int> Calc(int x);
inline std::pair<int, int> Merge(std::pair<int, int> x, std::pair<int, int> y);
inline void Update(int Index, int Left, int Right);
inline void BuildIntervalTree(int Index, int Left, int Right);
inline std::pair<int, int> Query(int Index, int Left, int Right, int L, int R);
inline std::pair<int, int> Query(int x);
inline void Modify(int Pos, int &k, int Delta);
inline bool Judge(int Pos, int k, int Delta);
inline void Change(int x, int y);
inline void Modify(int Index, int Left, int Right, int Pos);
int main() {
srand((unsigned long long)"非旋treap呀");
#ifdef DEBUG
NoneRotateTreap[0].Insert(1);
NoneRotateTreap[0].Insert(1);
NoneRotateTreap[0].Insert(3);
printf("%d %d ", NoneRotateTreap[0].Rank(2), NoneRotateTreap[0].Rank(1));
NoneRotateTreap[0].Insert(-INF);
printf("%d\n", NoneRotateTreap[0].Rank(1));
#endif
scanf("%d%d", &N, &k);
for (int i = 1; i < N; ++i) {
int x, y;
scanf("%d%d", &x, &y);
AddEdge(x, y);
AddEdge(y, x);
}
for (int i = 1; i <= N; ++i) scanf("%d", &C[i]);
scanf("%d", &Q);
CutDfs1(1, 0);
Top[1] = 1;
Dfn[1] = ++TreeSize;
Ref[TreeSize] = 1;
CutDfs2(1, 0);
for (int i = 1; i <= N; ++i) Bottom[Top[i]] = std::max(Bottom[Top[i]], Dfn[i]);
#ifdef _DEBUG_
printf("Father : ");
for (int i = 1; i <= N; ++i) printf("%d ", Father[i]); printf("\n");
printf(" Top : ");
for (int i = 1; i <= N; ++i) printf("%d ", Top[i]); printf("\n");
printf(" Dfn : ");
for (int i = 1; i <= N; ++i) printf("%d ", Dfn[i]); printf("\n");
printf("Bottom : ");
for (int i = 1; i <= N; ++i) printf("%d ", Bottom[i]); printf("\n");
#endif
BuildIntervalTree(1, 1, N);
#ifdef _DEBUG_
printf("Check Ans :\n");
for (int i = 1; i <= N; ++i) {
std::pair<int, int> Ans = Query(i);
printf("%d %d %d\n", i, Ans.first, Ans.second);
}
#endif
for (int i = 1; i <= N; ++i)
if (Top[i] == i)
Record[i] = Query(i).first;
for (int i = 1; i <= Q; ++i) {
int Opt, x, y;
scanf("%d", &Opt);
if (Opt == 1) {
scanf("%d", &x);
int Ans = Query(x).first > k ? 1 : 0;
printf("%d\n", Ans);
}
if (Opt == 2) {
scanf("%d%d", &x, &y);
Change(x, y);
}
if (Opt == 3) scanf("%d", &k);
#ifdef _DEBUG_
printf("After Opt %d (%d-th) :\n", Opt, i);
for (int j = 1; j <= N; ++j) {
std::pair<int, int> Ans = Query(j);
printf("%d %d %d\n", j, Ans.first, Ans.second);
}
#endif
}
return 0;
}
inline void AddEdge(int x, int y) {
Edge[++Used] = edge(y, Start[x]);
Start[x] = Used;
return;
}
inline void CutDfs1(int u, int Fa) {
Size[u] = 1;
Father[u] = Fa;
Deep[u] = Deep[Fa] + 1;
for (int t = Start[u]; t; t = Edge[t].Next) {
int v = Edge[t].To;
if (v == Fa) continue;
++CountSon[u];
CutDfs1(v, u);
Size[u] += Size[v];
if (Size[v] > Size[Son[u]])
Son[u] = v;
}
// if (Size[Son[u]] == 1) Son[u] = 0;
return;
}
inline void CutDfs2(int u, int Fa) {
if (Son[u]) {
Top[Son[u]] = Top[u];
Dfn[Son[u]] = ++TreeSize;
Ref[TreeSize] = Son[u];
CutDfs2(Son[u], u);
}
for (int t = Start[u]; t; t = Edge[t].Next) {
int v = Edge[t].To;
if (v == Fa || v == Son[u]) continue;
Top[v] = v;
Dfn[v] = ++TreeSize;
Ref[TreeSize] = v;
CutDfs2(v, u);
}
return;
}
inline std::pair<int, int> Merge(std::pair<int, int> x, std::pair<int, int> y) {
std::pair<int, int> Ans;
Ans.first = std::min(std::max(y.first, x.first), x.second);
Ans.second = std::min(std::max(y.second, x.first), x.second);
#ifdef _DEBUG_
printf(" Merge %d %d, %d %d, Get %d %d\n", x.first, x.second, y.first, y.second, Ans.first, Ans.second);
#endif
return Ans;
}
inline void Update(int Index, int Left, int Right) {
if (Left == Right) return;
IntervalTree[Index] = Merge(IntervalTree[Index << 1], IntervalTree[Index << 1 | 1]);
return;
}
inline void BuildIntervalTree(int Index, int Left, int Right) {
if (Left == Right) {
// IntervalTree[Index] = Calc(Ref[Left]);
Modify(1, 1, N, Left);
//Notice : 这里需要用 Modify 同时来上传。因为下面要用到 Query(Ref[Left]) 。而 Modify 至多被调用 n 次,每次 log n ,所以不会影响复杂度。
#ifdef _DEBUG_
printf("At Pos %d, Cal %d %d\n", Ref[Left], IntervalTree[Index].first, IntervalTree[Index].second);
#endif
if (Top[Ref[Left]] == Ref[Left]) {
// NoneRotateTreap[Father[Ref[Left]]].Insert(IntervalTree[Index].first);
NoneRotateTreap[Father[Ref[Left]]].Insert(Query(Ref[Left]).first);
#ifdef _DEBUG_
printf("*** %d\n", Ref[Left]);
printf("!!!CAL %d %d\n", Query(Ref[Left]).first, Query(Ref[Left]).second);
printf("Add %d To %d\n", Query(Ref[Left]).first, Father[Ref[Left]]);
NoneRotateTreap[Father[Ref[Left]]].Debug();
#endif
}
return;
}
int Mid = (Left + Right) >> 1;
BuildIntervalTree(Index << 1 | 1, Mid + 1, Right); //Right build first
//Notice : 处理数据的时候要先处理深度深的链。通过线段树上先算右边再算左边就可以做到
BuildIntervalTree(Index << 1, Left, Mid);
Update(Index, Left, Right);
return;
}
inline std::pair<int, int> Query(int Index, int Left, int Right, int L, int R) {
#ifdef _DEBUG_
printf(" Query %d %d %d %d %d\n", Index, Left, Right, L, R);
#endif
if (L <= Left && Right <= R) return IntervalTree[Index];
int Mid = (Left + Right) >> 1;
if (R <= Mid) return Query(Index << 1, Left, Mid, L, R);
if (L > Mid) return Query(Index << 1 | 1, Mid + 1, Right, L, R);
return Merge(Query(Index << 1, Left, Mid, L, R), Query(Index << 1 | 1, Mid + 1, Right, L, R));
}
inline std::pair<int, int> Calc(int x) {
if (C[x] == 0) return std::pair<int, int>(-INF, -INF);
if (C[x] == 1) return std::pair<int, int>(INF, INF);
// std::pair<int, int> Temp = Query(Son[x]);
std::pair<int, int> Ans = std::pair<int, int>(0, 0);
#ifdef DEBUG_
printf("Son white :\n");
#endif
Modify(x, Ans.first, 2);
#ifdef DEBUG_
printf("Son black :\n");
#endif
Modify(x, Ans.second, 0);
#ifdef DEBUG_
printf("Finally get : %d %d\n", Ans.first, Ans.second);
#endif
return Ans;
}
inline std::pair<int, int> Query(int x) {
#ifdef _DEBUG_
printf(" Query %d -> %d %d\n", x, Dfn[x], Bottom[Top[x]]);
#endif
return Query(1, 1, N, Dfn[x], Bottom[Top[x]]);
}
inline void Modify(int Pos, int &k, int Delta) {
while(!Judge(Pos, k, Delta)) ++k;
while(Judge(Pos, k - 1, Delta)) --k;
return;
}
inline bool Judge(int Pos, int k, int Delta) {
int m = CountSon[Pos] - k - Delta;
int c = NoneRotateTreap[Pos].Rank(k + 1) - 1;
#ifdef DEBUG_
printf(" Judge %d %d %d -> %d %d, %d < 0 ?\n", Pos, k, Delta, m, c, m - 2 * c);
#endif
#ifdef DEBUG
printf(" Treap[%d], QueryRank %d, Get %d\n", Pos, k + 1, NoneRotateTreap[Pos].Rank(k + 1));
#endif
return m - 2 * c < 0;
}
inline void Change(int x, int y) {
C[x] = y;
while (x) {
#ifdef _DEBUG_
printf("Change Pos %d\n", x);
#endif
Modify(1, 1, N, Dfn[x]);
#ifdef _DEBUG_
printf(" Elements in Treap : ");
NoneRotateTreap[Father[Top[x]]].Debug();
#endif
NoneRotateTreap[Father[Top[x]]].Delete(Record[Top[x]]);
#ifdef _DEBUG_
printf(" NoneRotateTreap %d Delete %d\n", Father[Top[x]], Record[Top[x]]);
printf(" Elements in Treap : ");
NoneRotateTreap[Father[Top[x]]].Debug();
#endif
NoneRotateTreap[Father[Top[x]]].Insert(Record[Top[x]] = Query(Top[x]).first);
#ifdef _DEBUG_
printf(" NoneRotateTreap %d Insert %d\n", Father[Top[x]], Record[Top[x]]);
printf(" Elements in Treap : ");
NoneRotateTreap[Father[Top[x]]].Debug();
#endif
x = Father[Top[x]];
}
return;
}
inline void Modify(int Index, int Left, int Right, int Pos) {
if (Left == Right) {
IntervalTree[Index] = Calc(Ref[Pos]);
#ifdef _DEBUG_
printf(" Modify %d, Get %d %d\n", Ref[Pos], IntervalTree[Index].first, IntervalTree[Index].second);
#endif
return;
}
int Mid = (Left + Right) >> 1;
if (Pos <= Mid) Modify(Index << 1, Left, Mid, Pos);
if (Pos > Mid) Modify(Index << 1 | 1, Mid + 1, Right, Pos);
Update(Index, Left, Right);
return;
}
CF1208 Red Blue Tree的更多相关文章
- CF1208H Red Blue Tree
CF1208H Red Blue Tree 原本应该放在这里但是这题过于毒瘤..单独开了篇blog 首先考虑如果 $ k $ 无限小,那么显然整个树都是蓝色的.随着 $ k $ 逐渐增大,每个点都会有 ...
- BNUOJ 26229 Red/Blue Spanning Tree
Red/Blue Spanning Tree Time Limit: 2000ms Memory Limit: 131072KB This problem will be judged on HDU. ...
- Red–black tree ---reference wiki
source address:http://en.wikipedia.org/wiki/Red%E2%80%93black_tree A red–black tree is a type of sel ...
- [转载] 红黑树(Red Black Tree)- 对于 JDK TreeMap的实现
转载自http://blog.csdn.net/yangjun2/article/details/6542321 介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf B ...
- Red Black Tree 红黑树 AVL trees 2-3 trees 2-3-4 trees B-trees Red-black trees Balanced search tree 平衡搜索树
小结: 1.红黑树:典型的用途是实现关联数组 2.旋转 当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质.为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些 ...
- 2018 ICPC青岛网络赛 B. Red Black Tree(倍增lca好题)
BaoBao has just found a rooted tree with n vertices and (n-1) weighted edges in his backyard. Among ...
- 计蒜客 Red Black Tree(树形DP)
You are given a rooted tree with n nodes. The nodes are numbered 1..n. The root is node 1, and m of ...
- Red Black Tree(红黑树)
(修改于 2018-05-06 15:53:22 还差删除维护操作.层序遍历没完成.维护操作没完成不想写层序遍历怎么办...) 今天下午完成了红黑树的插入的维护操作,但删除的维护操作还没有解决,删除的 ...
- ZOJ - 4048 Red Black Tree (LCA+贪心) The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online
题意:一棵树上有m个红色结点,树的边有权值.q次查询,每次给出k个点,每次查询有且只有一次机会将n个点中任意一个点染红,令k个点中距离红色祖先距离最大的那个点的距离最小化.q次查询相互独立. 分析:数 ...
随机推荐
- OpenCV安装和测试
参考链接:http://blog.csdn.net/bruce_zeng/article/details/7961153 OpenCv下载链接:http://sourceforge.net/proje ...
- vmware centos .net core sdk开发测试
环境:vmware + centos+ .net core2.0 一.安装.net core sdk sudo rpm -Uvh https://packages.microsoft.com/conf ...
- 【opencv 源码剖析】 四、 Mat的赋值构造函数 和 拷贝构造函数
1.赋值构造函数 右值引用 inline Mat& Mat::operator = (Mat&& m) { if (this == &m) return *this; ...
- phpspider爬虫框架的使用
这几天使用PHP的爬虫框架爬取了一些数据,发现还是挺方便的,先上爬虫框架的文档 phpspider框架文档 使用方法其实在文档中写的很清楚而且在demo中也有使用示例,这里放下我自己的代码做个笔记 & ...
- ThreeJS中创建文字的几种方法
1. DOM + CSS 传统html5的文字实现,用于添加描述性叠加文字的方法.一般使用绝对定位,并且保证z-index够大,用于显示在3D场景之上. 优点: 与CSS3D效果一致 缺点: 3d效果 ...
- css———详解height与line_height
定义 height指的是块级别元素的高度: line-height指的是元素内容的高度. height和line-height的联系 CSS中起高度作用的应该就是height以及line-height ...
- c# datatable 如何转CSV文件
public void DataTableToCSV(DataTable dtCSV, string csvFileFullName, bool writeHeader, string delimet ...
- 如何对Nginx日志文件进行切割保存
日积月累下,日志文件会越来越大,日志文件太大严重影响服务器效率,须要定时对日志文件进行切割. 切割的方式有按月切割.按天切割.按小时切割,一般都是按天切割. 那么如何进行切割呢? 思路: 创建日志文件 ...
- Java动态追踪技术探究(动态修改)
Java动态追踪技术探究 Java探针-Java Agent技术-阿里面试题 秒懂Java动态编程(Javassist研究) 可以用于在类加载的时候,修改字节码. Java agent(Java探针) ...
- async/await 处理多个网络请求同步问题
1.async/await是基于Promise的,是进一步的一种优化,await会等待异步执行完成 getProjectTask(id){ this.axios.get('/api/v1/task/' ...