题目链接: 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. 获取文件CRC和MD5

    unit untCRCMD5; interface { 获取文件CRC校验码 } function GetFileCRC(const iFileName: string): String; { 获取字 ...

  2. 瑕疵(bug)严重性定义

    ======================== 严重性定义缺陷: o 最先进的–造成执行中断(应用程序崩溃),该功能未预期实现,测试等工作无法进行. o 急-事件是非常重要的,须要立即给予关注. o ...

  3. Android自己定义控件而且使其能够在xml中自己定义属性

    为什么要自己定义View android开发中自己定义View的优点是显而易见的.比方说以下的这个顶部导航,它被设计出如今应用的每一个界面,但每次的内容却不尽同样.我们不能在每一个layout资源中都 ...

  4. C程序中唯一序列号的生成

    在实际的软件开发项目中.常常会涉及唯一序列号的生成.本文以一个实际的程序为例,介绍了唯一序列号的生成过程. 本文生成的序列号的样式为:MMDDHHMINSS_XXXXXX. 程序例如以下: /**** ...

  5. jboss 7 as1 日志配置

    原文地址:https://docs.jboss.org/author/display/AS71/Logging+Configuration Overview The overall server lo ...

  6. linux安装总结(亲测)

    一:磁盘分区情况 NTFS,FAT32 这是两种常用的磁盘格式 windows7自带的磁盘工具也可以分出上面的格式,用软件也可以.但是FAT32最大的复制文件进去的限度时4G. EXT3只能用软件才能 ...

  7. Windows开启Telnet

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/4301513.html ...

  8. 用Javascript进行HTML转义(分享)

      众所周知页面上的字符内容通常都需要进行HTML转义才能正确显示,尤其对于Input,Textarea提交的内容,更是要进行转义以防止javascript注入攻击.   通常的HTML转义主要是针对 ...

  9. 基于JAVA网络编程的聊天小程序

    package com.neusoft.edu.socket; import java.io.BufferedReader; import java.io.IOException; import ja ...

  10. 判断浏览器是否支持FileReader

    1.js代码: //判断浏览器是否支持FileReader if (typeof FileReader == "undefined") { document.write(" ...