模拟赛中遇到了这个题,当时我这个沙雕因为把一个\(y\)打成了\(x\)而爆零。回来重新写这道题,莫名其妙的拿了rank1。。。

我的解法与其他几位的题解有些不同我太蒻了。并没有选取所谓的关键点,而是用树链剖分将树上问题转化为序列的问题。


序列问题的解决

  • 现在的问题是,维护一个序列,要求查询若干个区间的并集的权值种类和\(mex\),无修改操作。
  • 如果只有一个区间,那么这个问题就可以很愉快的用主席树解决了。
  • 然而要查询的是区间并的权值种类和\(mex\),而且这两个东西都不满足区间可加性,于是可以放弃线段树的思路了。
  • 可以想到用分块解决这个问题,设每一块大小为\(B\)
  • 考虑到点权\(\leq 30000\),又要合并若干个区间,于是可以想到用\(bitset\)维护区间权值信息。
  • 设\(f[][]\)为一个\(bitset\)的二维数组,\(f[l][r]\)表示从第\(l\)块到第\(r\)块(包含\(l,r\))之间的权值集合,这个数组可以在\(O(n\frac{30000}{32})\)的时间内预处理出来。每次查询的时候,整块的信息可以在\(O(\frac{30000}{32})\)的时间内解决。
  • 剩余的部分直接暴力处理,时间复杂度为\(O(B)\)。这样单次查询可以做到\(O(\frac{30000}{32}+B)\)
  • 至于区间并,直接开一个全局变量记录答案(我的代码中用的是\(cur\)),依次处理每个区间。因为重叠的部分并不会对答案有影响。现在序列的问题已经解决了。

序列处理部分的代码

int b[N]; // 记录区间上每一个位置属于哪个块
int L[N], R[N]; // 每个块的左右断点
bitset<W> cur; // 查询时用到的全局变量
bitset<W> f[110][110]; // 预处理的 f 数组 void preWork() {
// 预处理
for (int i = 1; i <= n; ++i) {
// 先计算单个块的权值情况
b[i] = (i-1) / B + 1;
f[b[i]][b[i]].set(a[i]);
}
// 处理每个块的左右端点
for (int i = 1; i <= b[n]; ++i)
L[i] = R[i-1] + 1, R[i] = i * B;
R[b[n]] = n; // 最后一个块的右端点要特判
for (int i = 1; i < b[n]; ++i)
for (int j = i+1; j <= b[n]; ++j) // 计算 f 数组
f[i][j] = f[i][j-1] | f[j][j];
} void queryOnBlock(int l, int r) {
if (b[l] == b[r]) {
// 特判左右端点在同一个块内的情况
for (int i = l; i <= r; ++i) cur.set(a[i]);
return;
}
cur |= f[b[l]+1][b[r]-1]; // 两块之间的部分直接查询
for (int i = l; i <= R[b[l]]; ++i) cur.set(a[i]); // 左边的剩余部分
for (int i = L[b[r]]; i <= r; ++i) cur.set(a[i]); // 右边的剩余部分
} int mex(bitset<W> &s) {
// 暴力求 mex
for (int i = 0; i < W; ++i)
if (!s.test(i)) return i;
return 1e9;
}

将树上问题转化为序列问题

树链剖分的板子(我这个蒟蒻写挂的部分)。。。

直接贴代码了

int G[N], ed = 1, w[N]; // 树的存储
struct Edge {
int to, nxt;
Edge() { to = nxt = 0; }
Edge(int to, int nxt) : to(to), nxt(nxt) {}
} e[N<<1]; inline void addEdge(int x, int y) {
e[++ed] = Edge(y, G[x]), G[x] = ed;
e[++ed] = Edge(x, G[y]), G[y] = ed;
} // 树链剖分相关
int dfn[N]; // dfs 序
int fa[N]; // 父结点
int son[N]; // 重儿子
int top[N]; // 重链顶端
int size[N]; // 子数大小
int dep[N]; // 深度
int a[N]; // 转化的序列 void dfs1(int x, int p) {
size[x] = 1, fa[x] = p;
for (int i = G[x]; i != 0; i = e[i].nxt) {
int y = e[i].to;
if (y == p) continue;
dep[y] = dep[x] + 1;
dfs1(y, x);
size[x] += size[y];
if (size[son[x]] < size[y])
son[x] = y;
}
} void dfs2(int x, int t) {
static int cur = 0;
dfn[x] = ++cur, a[cur] = w[x], top[x] = t;
if (!son[x]) return;
dfs2(son[x], t);
for (int i = G[x]; i != 0; i = e[i].nxt) {
int y = e[i].to;
if (y == son[x] || y == fa[x]) continue;
dfs2(y, y);
}
} void queryOnTree(int x, int y) {
// 树上查询
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
queryOnBlock(dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
if (dfn[x] > dfn[y]) swap(x, y);
queryOnBlock(dfn[x], dfn[y]);
}

总代码

// 2598ms 57.76MB 无O2
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <bitset>
#define LL long long
using namespace std; inline int getint() {
int x = 0, f = 1; char c = getchar();
while (!isdigit(c)) { if (c == '-') f = 0; c = getchar(); }
while (isdigit(c)) { x = (x*10) + (c-'0'); c = getchar(); }
return f ? x : -x;
} const int N = 1e5 + 10;
const int B = 1e3;
const int W = 30001; int n, m, flag; // flag 判断是否需要异或
int ans1, ans2, lastans; // ans1 为权值种类,ans2 为权值 mex int G[N], ed = 1, w[N]; // 树的存储
struct Edge {
int to, nxt;
Edge() { to = nxt = 0; }
Edge(int to, int nxt) : to(to), nxt(nxt) {}
} e[N<<1]; inline void addEdge(int x, int y) {
e[++ed] = Edge(y, G[x]), G[x] = ed;
e[++ed] = Edge(x, G[y]), G[y] = ed;
} // 树链剖分相关
int dfn[N]; // dfs 序
int fa[N]; // 父结点
int son[N]; // 重儿子
int top[N]; // 重链顶端
int size[N]; // 子数大小
int dep[N]; // 深度
int a[N]; // 转化的序列 void dfs1(int x, int p) {
size[x] = 1, fa[x] = p;
for (int i = G[x]; i != 0; i = e[i].nxt) {
int y = e[i].to;
if (y == p) continue;
dep[y] = dep[x] + 1;
dfs1(y, x);
size[x] += size[y];
if (size[son[x]] < size[y])
son[x] = y;
}
} void dfs2(int x, int t) {
static int cur = 0;
dfn[x] = ++cur, a[cur] = w[x], top[x] = t;
if (!son[x]) return;
dfs2(son[x], t);
for (int i = G[x]; i != 0; i = e[i].nxt) {
int y = e[i].to;
if (y == son[x] || y == fa[x]) continue;
dfs2(y, y);
}
} int b[N]; // 记录区间上每一个位置属于哪个块
int L[N], R[N]; // 每个块的左右断点
bitset<W> cur; // 查询时用到的全局变量
bitset<W> f[110][110]; // 预处理的 f 数组 void preWork() {
// 预处理
for (int i = 1; i <= n; ++i) {
// 先计算单个块的权值情况
b[i] = (i-1) / B + 1;
f[b[i]][b[i]].set(a[i]);
}
// 处理每个块的左右端点
for (int i = 1; i <= b[n]; ++i)
L[i] = R[i-1] + 1, R[i] = i * B;
R[b[n]] = n; // 最后一个块的右端点要特判
for (int i = 1; i < b[n]; ++i)
for (int j = i+1; j <= b[n]; ++j) // 计算 f 数组
f[i][j] = f[i][j-1] | f[j][j];
} void queryOnBlock(int l, int r) {
if (b[l] == b[r]) {
// 特判左右端点在同一个块内的情况
for (int i = l; i <= r; ++i) cur.set(a[i]);
return;
}
cur |= f[b[l]+1][b[r]-1]; // 两块之间的部分直接查询
for (int i = l; i <= R[b[l]]; ++i) cur.set(a[i]); // 左边的剩余部分
for (int i = L[b[r]]; i <= r; ++i) cur.set(a[i]); // 右边的剩余部分
} void queryOnTree(int x, int y) {
// 树上查询
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
queryOnBlock(dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
if (dfn[x] > dfn[y]) swap(x, y);
queryOnBlock(dfn[x], dfn[y]);
} int mex(bitset<W> &s) {
// 暴力求 mex
for (int i = 0; i < W; ++i)
if (!s.test(i)) return i;
return 1e9;
} int main() {
cin >> n >> m >> flag;
for (int i = 1; i <= n; ++i) w[i] = getint();
for (int i = 1; i < n; ++i) {
int x = getint(), y = getint();
addEdge(x, y);
}
dfs1(1, 0), dfs2(1, 1);
preWork();
for (int i = 1; i <= m; ++i) {
cur.reset();
int num = getint();
for (int j = 1; j <= num; ++j) {
int x = getint() ^ (flag*lastans);
int y = getint() ^ (flag*lastans);
queryOnTree(x, y);
}
ans1 = cur.count(), ans2 = mex(cur);
lastans = ans1 + ans2;
printf("%d %d\n", ans1, ans2);
}
return 0;
}

[题解] 洛谷 P3603 雪辉的更多相关文章

  1. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  2. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

  3. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  4. 题解-洛谷P4229 某位歌姬的故事

    题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...

  5. 题解-洛谷P4724 【模板】三维凸包

    洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...

  6. 题解-洛谷P4859 已经没有什么好害怕的了

    洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...

  7. 题解-洛谷P5217 贫穷

    洛谷P5217 贫穷 给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作: \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\). \(\te ...

  8. 题解 洛谷 P2010 【回文日期】

    By:Soroak 洛谷博客 知识点:模拟+暴力枚举 思路:题目中有提到闰年然后很多人就认为,闰年是需要判断的其实,含有2月29号的回文串,前四位是一个闰年那么我们就可以直接进行暴力枚举 一些小细节: ...

  9. 题解 洛谷P2158 【[SDOI2008]仪仗队】

    本文搬自本人洛谷博客 题目 本文进行了一定的更新 优化了 Markdown 中 Latex 语句的运用,加强了可读性 补充了"我们仍不曾知晓得 消失的 性质5 ",加强了推导的严谨 ...

随机推荐

  1. 用 S5PV210 学习 Linux (二) 刷机(二)

    1.在 Ubuntu 下 ,进入  dnw-linux-master\src\driver 文件下,make 截图 如下: 2.紧接着 加载该模块到内核(注意:需要root权限),sudo insmo ...

  2. HTML5手机端拍照上传

    1.accept="image/*" capture="camera" 自动调用手机端拍照功能 accept="image/*" captu ...

  3. SPOJ 4487. Can you answer these queries VI splay

    题目链接:点击打开链接 题意比較明显,不赘述. 删除时能够把i-1转到根,把i+1转到根下 则i点就在 根右子树 的左子树,且仅仅有i这一个 点 #include<stdio.h> #in ...

  4. 大话Linux内核中锁机制之信号量、读写信号量

    大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实 ...

  5. 如何快速找到指定端口被哪个程序占用并释放该端口(解决bindException)

    首先打开打开任务管理器,选择性能模块,下方有打开资源监视器,或者直接搜索资源监视器 在资源监视器中点击侦听端口模块,即可看到正在使用网络端口的应用程序名和pid,如果被占用可以直接使用命令行关闭即可 ...

  6. AutoMapper 帮助类

    AutoMapper帮助类 /// <summary> /// AutoMapper帮助类 /// </summary> public static class AutoMap ...

  7. 【Dubbo源码阅读系列】之 Dubbo SPI 机制

    最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解.如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出. Dubbo SPI 介绍 Java ...

  8. 怎么在苹果Mac虚拟机上安装Win7

    怎么在苹果Mac虚拟机上安装Win7 使用 Mac 系统的用户,因为一些软件或是应用的原因,可能需要 Windows 系统才能完成.那如果不想在自己的 Mac 电脑上安装双启动系统的话,我们还可以在 ...

  9. C++11 initializer_list 和 Range-based for loop 学习理解

    win10 + vs2017 源码如下: int main() { vector< int > numbers = { 1, 2, 3, 4, 5 }; for (auto num : n ...

  10. Linux单用户CS模型TCP通讯完全注释手册

    Linux单用户CS模型TCP通讯完全注释手册 server 描述 实现一个简单的Linux单用户CS通讯,客户端发送一串字符串,服务器将其转换为大写后返回. server 代码 ``` #inclu ...