bzoj4129 Haruna’s Breakfast 树上带修莫队+分块
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=4129
题解
考虑没有修改的序列上的版本应该怎么做:
弱化的题目应该是这样的:
给定一个序列,每次询问区间 \([l, r]\) 中元素的最小没有出现的自然数。
这个弱化的版本可以用离线+线段树二分水掉。但是这个做法显然不太好搬到树上做。
上面的弱化版还有一个莫队做法:可以用莫队维护出来每一个区间的每一个数的出现为次数。把出现过的数通过分块表示出来,于是查询的时候枚举每一个块,寻找第一个不满的块。
那么放到树上以后,就用树上莫队吧。
既然还有修改,那么就是树上带修莫队。
注意一下:
因为数据范围比较大,所以需要离散化。但是离散化的时候为了防止漏掉一些数(某两个数在数轴上是不相邻的,但是在离散化以后相邻),需要把出现过的每一个数 \(+1\) 放进被离散化的序列中。
UPD:
貌似不需要离散化,答案一定 \(< n\),所以只需要保留 \(<n\) 的数就可以了。
我真的有点智商低下。
一定要考虑题目中的一些量是否有用,是否可以简化。
时间复杂度 \(O(m\cdot n^{\frac 23})\)。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 5e4 + 7;
const int B = 200 + 7;
#define bl(x) (((x) - 1) / blo + 1)
#define st(x) (((x) - 1) * blo + 1)
#define ed(x) std::min((x) * blo, dis)
int n, m, dis, dfc, dfc2, m1, m2, blo, now;
int a[N], b[N << 2], ans[N];
int dep[N], f[N], siz[N], son[N], dfn[N], pre[N], top[N];
int ldfn[N], rdfn[N], seq[N << 1];
int used[N], s[N << 2], ss[B], cnt[N << 2];
struct Update { int id, x, k, pre; } u[N];
struct Query {
int id, l, r, lca, *ans;
inline bool operator < (const Query &b) const {
if (bl(l) != bl(b.l)) return l < b.l;
if (bl(r) != bl(b.r)) return r < b.r;
return id < b.id;
}
} q[N];
struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
inline void dfs1(int x, int fa = 0) {
dep[x] = dep[fa] + 1, f[x] = fa, siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
ldfn[x] = ++dfc2, seq[dfc2] = x;
if (!son[x]) return (void)(rdfn[x] = ++dfc2, seq[dfc2] = x); dfs2(son[x], pa);
for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
rdfn[x] = ++dfc2, seq[dfc2] = x;
}
inline int lca(int x, int y) {
while (top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = f[top[x]] : y = f[top[y]];
return dep[x] < dep[y] ? x : y;
}
inline void qadd(int x, int k) {
s[x] += k, ss[bl(x)] += k;
assert(s[x] == 0 || s[x] == 1);
assert(ss[bl(x)] <= ed(bl(x)) - st(bl(x)) + 1 && ss[bl(x)] >= 0);
}
inline int qans() {
for (int i = 1; i <= bl(dis); ++i) if (ss[i] != ed(i) - st(i) + 1)
for (int j = st(i); j <= ed(i); ++j) if (!s[j]) return b[j - 1] + 1;
return b[dis] + 1;
}
inline void madd(int x) {
++cnt[x];
if (cnt[x] == 1) qadd(x, 1);
}
inline void mdel(int x) {
--cnt[x];
if (cnt[x] == 0) qadd(x, -1);
}
inline void mupd(int x) {
if (used[x]) mdel(a[x]);
else madd(a[x]);
used[x] ^= 1;
}
inline void cadd(int i) {
int x = u[i].x, k = u[i].k;
if (used[x]) mdel(a[x]);
u[i].pre = a[x], a[x] = k;
if (used[x]) madd(a[x]);
}
inline void cdel(int i) {
int x = u[i].x;
if (used[x]) mdel(a[x]);
assert(a[x] == u[i].k);
a[x] = u[i].pre;
if (used[x]) madd(a[x]);
}
inline void cupd(int i) {
while (now < m1 && u[now + 1].id <= i) cadd(++now);
while (now && u[now].id > i) cdel(now--);
}
inline void lsh() {
for (int i = 1; i <= dis; ++i) b[i + dis] = b[i] + 1;
dis *= 2;
std::sort(b + 1, b + dis + 1);
dis = std::unique(b + 1, b + dis + 1) - b - 1;
for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
for (int i = 1; i <= m1; ++i) u[i].k = std::lower_bound(b + 1, b + dis + 1, u[i].k) - b;
b[0] = -1;
}
inline void work() {
lsh();
blo = pow(n, 2.0 / 3);
std::sort(q + 1, q + m2 + 1);
int l = 1, r = 0;
for (int i = 1; i <= m2; ++i) {
cupd(q[i].id);
while (l > q[i].l) mupd(seq[--l]);
while (r < q[i].r) mupd(seq[++r]);
while (l < q[i].l) mupd(seq[l++]);
while (r > q[i].r) mupd(seq[r--]);
if (q[i].lca) assert(!used[q[i].lca]);
if (q[i].lca) mupd(q[i].lca);
*q[i].ans = qans();
if (q[i].lca) mupd(q[i].lca);
}
for (int i = 1; i <= m2; ++i) printf("%d\n", ans[i]);
}
inline void init() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]), b[++dis] = a[i];
int x, y;
for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
dfs1(1), dfs2(1, 1);
for (int i = 1; i <= m; ++i) {
int opt;
read(opt);
if (opt == 0) ++m1, read(u[m1].x), read(u[m1].k), u[m1].id = i, b[++dis] = u[m1].k;
else {
int x, y, p;
read(x), read(y);
if (ldfn[x] > ldfn[y]) std::swap(x, y);
p = lca(x, y), ++m2;
if (p == x) q[m2] = (Query){ i, ldfn[x], ldfn[y], 0, ans + m2 };
else q[m2] = (Query){ i, rdfn[x], ldfn[y], p, ans + m2 };
}
}
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
bzoj4129 Haruna’s Breakfast 树上带修莫队+分块的更多相关文章
- BZOJ 4129 Haruna’s Breakfast ( 树上带修莫队 )
题面 求树上某路径上最小的没出现过的权值,有单点修改 添加链接描述 分析 树上带修莫队板题,问题是怎么求最小的没出现过的权值. 因为只有nnn个点,所以没出现过的最小值一定在[0,n][0,n][0, ...
- [BZOJ4129]Haruna’s Breakfast(树上带修改莫队)
BZOJ3585,BZOJ2120,BZOJ3757三合一. 对于树上路径问题,树链剖分难以处理的时候,就用树上带修改莫队. 这里的MEX问题,使用BZOJ3585的分块方法,平衡了时间复杂度. 剩下 ...
- 【BZOJ-3052】糖果公园 树上带修莫队算法
3052: [wc2013]糖果公园 Time Limit: 200 Sec Memory Limit: 512 MBSubmit: 883 Solved: 419[Submit][Status] ...
- BZOJ3052: [wc2013]糖果公园【树上带修莫队】
Description Input Output Sample Input Sample Input Sample Output 84 131 27 84 HINT 思路 非常模板的树上带修莫队 真的 ...
- luogu4074 [WC2013]糖果公园(树上带修莫队)
link 题目大意:给一个树,树上每个点都有一种颜色,每个颜色都有一个收益 每次修改一个点上的颜色 或询问一条链上所有颜色第i次遇到颜色j可以获得w[i]*v[j]的价值,求链上价值和 题解:树上带修 ...
- BZOJ 3052/Luogu P4074 [wc2013]糖果公园 (树上带修莫队)
题面 中文题面,难得解释了 BZOJ传送门 Luogu传送门 分析 树上带修莫队板子题... 开始没给分块大小赋初值T了好一会... CODE #include <bits/stdc++.h&g ...
- LUOGU P4074 [WC2013]糖果公园 (树上带修莫队)
传送门 解题思路 树上带修莫队,搞了两天..终于开O2+卡常大法贴边过了...bzoj上跑了183s..其实就是把树上莫队和带修莫队结合到一起,首先求出括号序,就是进一次出一次那种的,然后如果求两个点 ...
- BZOJ 3052 树上带修莫队
思路: 就是把带修莫队移到了树上 块的大小开到(n^2/3)/2 比较好- 这是一个卡OJ好题 //By SiriusRen #include <cmath> #include <c ...
- 牛客挑战赛48E-速度即转发【带修莫队,分块】
正题 题目链接:https://ac.nowcoder.com/acm/contest/11161/E 题目大意 给出\(n\)个数字的一个序列,\(m\)个操作. 给出\(l,r,k\),求一个最大 ...
随机推荐
- office toolkit怎么用(以激活office professional 2013为例)
第一步:双击打开office toolkit工具,并选择office按钮(激活windows10选择windows按钮) 第二步:选择相应的office版本,我电脑安装的是Microsoft Offi ...
- 2018 icpc 青岛
https://zoj.pintia.cn/contests/91827364639/problems C 要把这两个二进制串变为相同,需要先看哪些位置不同,设为数组c,某位为1则两位不同. 分1形成 ...
- Win7上防火墙开放FTP服务以及ping解决方案
1.windows 防火墙开放ftp服务 The following 4 steps will allow both non-secure and SSL FTP traffic through fi ...
- java网络通信:伪异步I/O编程(PIO)
缺点:避免了线程资源耗尽的问题,但是根本上来说,serversocket的accept方法和inputstream的输入流方法都是阻塞型方法. 服务端:加了一个线程池,实现线程复用.客户端不变 pub ...
- Selenium学习之==>18种定位方式的使用
Selenium的定位方式一共有18种,单数8种,复数8种,最后两种是前面这16种的底层封装 单数形式8种 # 1.id定位 al = driver.find_element_by_id('i1') ...
- 关于MySQL的安装使用心得
MySQL浅浅地学习了几天,当然还是转到正轨Java上来了,昨天打了一串代码,测试注解来着,结果MySQL挂了~~~ 如何干净卸载MySQL帖子有很多,不再赘述,注册表是个好东西~~ 卸载了Mysql ...
- 几个 BeanUtils 中的坑,千万别踩!
背景 最近项目中在和第三方进行联调一个接口,我们这边发送http请求给对方,然后接收对方的回应,代码都是老代码. 根据注释,对方的SDK中写好的Request类有一个无法序列化的bug,所以这边重新写 ...
- 携程的 Dubbo 之路,值得学习!
注:本篇文章整理自董艺荃在 Dubbo 社区开发者日上海站的演讲. 1.缘起 携程当初为什么要引入 Dubbo 呢?实际上从 2013 年底起,携程内主要使用的就是基于 HTTP 协议的 SOA 微服 ...
- MySQL 的自增 ID 用完了,怎么办?
一.简述 在 MySQL 中用很多类型的自增 ID,每个自增 ID 都设置了初始值.一般情况下初始值都是从 0 开始,然后按照一定的步长增加.在 MySQL 中只要定义了这个数的字节长度,那么就会 ...
- 如何遍历div里面的文本内容,用each方法,
如何遍历div里面的文本内容,然后进行匹配传来的数据,进行选中div,并进行CSS样式处理, for(i = 0; i< $(".itemMenuRowBox").child ...