题目链接: BZOJ - 2819

题目分析

我们知道,单纯的 Nim 的必胜状态是,各堆石子的数量异或和不为 0 。那么这道题其实就是要求求出树上的两点之间的路径的异或和。要求支持单点修改。

方法一:树链剖分

这道题用树链剖分显然是可以做的,并且也很好写。
我刚开始写完之后又 WA 了,又是线段树写错了!!这次是建树的时候写错了!

Warning!Warning!

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm> using namespace std; const int MaxN = 500000 + 5; int n, m, Index;
int A[MaxN], Num[MaxN], Father[MaxN], Depth[MaxN], Size[MaxN], Top[MaxN], Son[MaxN], Pos[MaxN];
int T[MaxN * 4]; struct Edge
{
int v;
Edge *Next;
} E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y) {
++P; P -> v = y;
P -> Next = Point[x]; Point[x] = P;
} int DFS_1(int x, int Dep, int Fa) {
Depth[x] = Dep; Father[x] = Fa;
Size[x] = 1;
int SonSize, MaxSonSize;
SonSize = MaxSonSize = 0;
for (Edge *j = Point[x]; j; j = j -> Next) {
if (j -> v == Fa) continue;
SonSize = DFS_1(j -> v, Dep + 1, x);
if (SonSize > MaxSonSize) {
MaxSonSize = SonSize;
Son[x] = j -> v;
}
Size[x] += SonSize;
}
return Size[x];
} void DFS_2(int x) {
if (x == 0) return;
if (x == Son[Father[x]]) Top[x] = Top[Father[x]];
else Top[x] = x;
Pos[x] = ++Index;
Num[Pos[x]] = A[x];
DFS_2(Son[x]);
for (Edge *j = Point[x]; j; j = j -> Next) {
if (j -> v == Father[x] || j -> v == Son[x]) continue;
DFS_2(j -> v);
}
} void Build_Tree(int x, int s, int t) {
if (s == t) {
T[x] = Num[s];
return;
}
int m = (s + t) >> 1;
Build_Tree(x << 1, s, m);
Build_Tree(x << 1 | 1, m + 1, t);
T[x] = T[x << 1] ^ T[x << 1 | 1];
} void Change(int x, int s, int t, int a, int b) {
if (s == t) {
T[x] = b;
return;
}
int m = (s + t) >> 1;
if (a <= m) Change(x << 1, s, m, a, b);
else Change(x << 1 | 1, m + 1, t, a, b);
T[x] = T[x << 1] ^ T[x << 1 | 1];
} int Query(int x, int s, int t, int l, int r) {
if (l <= s && r >= t) return T[x];
int m = (s + t) >> 1;
int ret = 0;
if (l <= m) ret ^= Query(x << 1, s, m, l, r);
if (r >= m + 1) ret ^= Query(x << 1 | 1, m + 1, t, l, r);
return ret;
} bool EQuery(int x, int y) {
int fx, fy, Temp;
Temp = 0;
while (true) {
fx = Top[x]; fy = Top[y];
if (Depth[fx] < Depth[fy]) {
swap(fx, fy);
swap(x, y);
}
if (fx == fy) {
if (Pos[x] < Pos[y]) Temp ^= Query(1, 1, n, Pos[x], Pos[y]);
else Temp ^= Query(1, 1, n, Pos[y], Pos[x]);
break;
}
else {
Temp ^= Query(1, 1, n, Pos[fx], Pos[x]);
x = Father[fx];
}
}
if (Temp != 0) return true;
return false;
} int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &A[i]);
int a, b;
for (int i = 1; i <= n - 1; ++i) {
scanf("%d%d", &a, &b);
AddEdge(a, b);
AddEdge(b, a);
}
DFS_1(1, 0, 0);
Index = 0;
DFS_2(1);
Build_Tree(1, 1, n);
scanf("%d", &m);
char ch;
for (int i = 1; i <= m; ++i) {
ch = '#';
while (ch != 'C' && ch != 'Q') ch = getchar();
scanf("%d%d", &a, &b);
if (ch == 'C') Change(1, 1, n, Pos[a], b);
else {
if (EQuery(a, b)) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}

  

方法二:DFS序

我们可以维护每个点 x 到根节点的路径的异或和 f(x),那么对于从 a 点到 b 点的路径,我们先求出 a 和 b 的 LCA。那么答案就是 f(a) ^ f(b) ^ A[LCA(a, b)] 。因为在 f(a) 与 f(b) 中, f(LCA(a, b)) 其实没有被算入答案(因为抑或了两次就抵消了),所以再抑或一次将其补上。

对于每次的单点修改,只会影响它的子树的 f 值,所以就可以树状数组搞一下?

我想知道的是..这个样例为何这么神奇..不管有什么离谱的错误都能过样例...简直可怕..

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm> using namespace std; const int MaxN = 500000 + 15, MaxLog = 22; int n, m, Index, MaxT;
int Pos1[MaxN], Pos2[MaxN], Depth[MaxN], Father[MaxN], A[MaxN], T[MaxN * 2], Jump[MaxN][MaxLog + 3]; struct Edge
{
int v;
Edge *Next;
} E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y) {
++P; P -> v = y;
P -> Next = Point[x]; Point[x] = P;
} inline void Change(int x, int Num) {
for (int i = x; i <= MaxT; i += i & -i)
T[i] ^= Num;
} inline int Get(int x) {
int ret = 0;
for (int i = x; i; i -= i & -i)
ret ^= T[i];
return ret;
} void DFS(int x, int Dep, int Fa) {
Father[x] = Fa; Depth[x] = Dep;
Pos1[x] = ++Index;
Change(Pos1[x], A[x]);
for (Edge *j = Point[x]; j; j = j -> Next) {
if (j -> v == Fa) continue;
DFS(j -> v, Dep + 1, x);
}
Pos2[x] = ++Index;
Change(Pos2[x], A[x]);
} void Prepare_LCA() {
for (int i = 1; i <= n; ++i) Jump[i][0] = Father[i];
for (int j = 1; j <= MaxLog; ++j)
for (int i = 1; i <= n; ++i)
Jump[i][j] = Jump[Jump[i][j - 1]][j - 1];
} int LCA(int x, int y) {
int Dif;
if (Depth[x] < Depth[y]) swap(x, y);
Dif = Depth[x] - Depth[y];
if (Dif) {
for (int i = 0; i <= MaxLog; ++i)
if (Dif & (1 << i)) x = Jump[x][i];
}
if (x == y) return x;
for (int i = MaxLog; i >= 0; --i) {
if (Jump[x][i] != Jump[y][i]) {
x = Jump[x][i];
y = Jump[y][i];
}
}
return Father[x];
} int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &A[i]);
int a, b;
for (int i = 1; i <= n - 1; ++i) {
scanf("%d%d", &a, &b);
AddEdge(a, b);
AddEdge(b, a);
}
MaxT = n * 2 + 5;
Index = 0;
DFS(1, 0, 0);
Prepare_LCA();
scanf("%d", &m);
char ch;
int Temp;
for (int i = 1; i <= m; ++i) {
ch = '#';
while (ch != 'C' && ch != 'Q') ch = getchar();
scanf("%d%d", &a, &b);
if (ch == 'C') {
Change(Pos1[a], A[a]);
Change(Pos2[a], A[a]);
A[a] = b;
Change(Pos1[a], A[a]);
Change(Pos2[a], A[a]);
}
else {
Temp = Get(Pos1[a]) ^ Get(Pos1[b]) ^ A[LCA(a, b)];
if (Temp != 0) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}

  

[BZOJ - 2819] Nim 【树链剖分 / DFS序】的更多相关文章

  1. BZOJ 3083: 遥远的国度(树链剖分+DFS序)

    可以很显而易见的看出,修改就是树链剖分,而询问就是在dfs出的线段树里查询最小值,但由于这道题会修改根节点,所以在查询的时候需判断x是否为root的祖先,如果不是就直接做,是的话应该查询从1-st[y ...

  2. BZOJ 3083: 遥远的国度 [树链剖分 DFS序 LCA]

    3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 1280 MBSubmit: 3127  Solved: 795[Submit][Status][Discu ...

  3. BZOJ 2819 Nim 树链剖分+树状数组

    这题真没什么意思. 不过就是将普通的求Min,Max,求和等东西换成Xor,偏偏Xor还有很多性质. 算是刷道水题吧. #include<iostream> #include<cst ...

  4. 树链剖分&dfs序

    树上问题 很多处理区间的问题(像是RMQ,区间修改).可以用线段树,树状数组,ST表这些数据结构来维护.但是如果将这些问题挪到了树上,就不能直接用这些数据结构来处理了.这时就用到了dfs序和树链剖分. ...

  5. BZOJ_4034 [HAOI2015]树上操作 【树链剖分dfs序+线段树】

    一 题目 [HAOI2015]树上操作 二 分析 树链剖分的题,这里主要用到了$dfs$序,这题比较简单的就是不用求$lca$. 1.和树链剖分一样,先用邻接链表建双向图. 2.跑两遍$dfs$,其实 ...

  6. 树链剖分||dfs序 各种题

    1.[bzoj4034][HAOI2015]T2 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把 ...

  7. BZOJ:2819 NIM(树链剖分||DFS序 &&NIM博弈)

    著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是v ...

  8. BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1352  Solved: 780[Submit][Stat ...

  9. bzoj 2819 Nim(BIT,dfs序,LCA)

    2819: Nim Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1596  Solved: 597[Submit][Status][Discuss] ...

随机推荐

  1. Oracle char 查询问题

    近期碰到一个问题,数据库有个字段设置类型是char(2),然后数据库保存的数据位1,当使用hibernate 查询时 我使用query.setString("permissionLevel& ...

  2. Android UI学习 - GridView和ImageView的使用

    GridView: A view that shows items in two-dimensional scrolling grid. The items in the grid come from ...

  3. Android-自己定义图像资源的使用(1)

    Android-自己定义图像资源的使用 2014年4月28日 周一 天气晴朗 心情平静 本篇博文给大家介绍一下,在Android开发中经经常使用到的一些图像资源,具体内容麻烦请各位认真查看官网,下面附 ...

  4. mybatis0207 resultType、resultMap、延迟加载使用场景总结

    延迟加载: 延迟加载实现的方法多种多样,在只查询单表就可以满足需求,为了提高数据库查询性能使用延迟加载,再查询关联信息. mybatis提供延迟加载的功能用于service层. resultType: ...

  5. 为什么objc_msgSend必须用汇编实现

    译者前言 总是看到有人说用汇编实现objc_msgSend是为了速度快,当然这个不可否认.但是难道没有别的原因?于是就看到了这篇文章,遂翻译之!=.= 我自己的理解就是,用汇编实现,是为了应对不同的“ ...

  6. ASP.NET MVC5框架揭秘 学习笔记01

    1.自治视图 在早期(作者也没说明是多早,自己猜吧),我们倾向于将所有与UI相关的操作糅合在一起(现在我身边还有很多人这样做),这些操作包括UI界面的呈现. 用户交互操作的捕捉与响应(UI处理逻辑). ...

  7. Linux 查看系统硬件信息(实例详解)

    原文链接:http://www.cnblogs.com/ggjucheng/archive/2013/01/14/2859613.html linux查看系统的硬件信息,并不像windows那么直观, ...

  8. spring集成 log4j + slf4j

    以maven web项目为例, 首先.在pom文件引入相关依赖,如下(spring官网文档有介绍): <dependencies> <!-- spring 相关 --> < ...

  9. 强大的Core Image框架,各种滤镜处理图像

    首先介绍一下Core Image,他是一个很强大的图像处理框架,他可以让你简单的应用各种滤镜来处理图像,比如说色相,饱和度,亮度等等...他是运用GPU(CPU)实时地处理图像数据和视频的帧.而且Co ...

  10. Linux系统下分割tomcat日志

    在Linux系统下,tomcat日志catalina.out并不会像window系统下,按日期进行重写备份,因此在Linux系统下会造成日志文件过大的情况,本文介绍采用 cronolog工具进行如在w ...