[BZOJ - 2819] Nim 【树链剖分 / DFS序】
题目链接: 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序】的更多相关文章
- BZOJ 3083: 遥远的国度(树链剖分+DFS序)
可以很显而易见的看出,修改就是树链剖分,而询问就是在dfs出的线段树里查询最小值,但由于这道题会修改根节点,所以在查询的时候需判断x是否为root的祖先,如果不是就直接做,是的话应该查询从1-st[y ...
- BZOJ 3083: 遥远的国度 [树链剖分 DFS序 LCA]
3083: 遥远的国度 Time Limit: 10 Sec Memory Limit: 1280 MBSubmit: 3127 Solved: 795[Submit][Status][Discu ...
- BZOJ 2819 Nim 树链剖分+树状数组
这题真没什么意思. 不过就是将普通的求Min,Max,求和等东西换成Xor,偏偏Xor还有很多性质. 算是刷道水题吧. #include<iostream> #include<cst ...
- 树链剖分&dfs序
树上问题 很多处理区间的问题(像是RMQ,区间修改).可以用线段树,树状数组,ST表这些数据结构来维护.但是如果将这些问题挪到了树上,就不能直接用这些数据结构来处理了.这时就用到了dfs序和树链剖分. ...
- BZOJ_4034 [HAOI2015]树上操作 【树链剖分dfs序+线段树】
一 题目 [HAOI2015]树上操作 二 分析 树链剖分的题,这里主要用到了$dfs$序,这题比较简单的就是不用求$lca$. 1.和树链剖分一样,先用邻接链表建双向图. 2.跑两遍$dfs$,其实 ...
- 树链剖分||dfs序 各种题
1.[bzoj4034][HAOI2015]T2 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把 ...
- BZOJ:2819 NIM(树链剖分||DFS序 &&NIM博弈)
著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是v ...
- BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]
4196: [Noi2015]软件包管理器 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1352 Solved: 780[Submit][Stat ...
- bzoj 2819 Nim(BIT,dfs序,LCA)
2819: Nim Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1596 Solved: 597[Submit][Status][Discuss] ...
随机推荐
- Nunit Test 项目模块
下载地址:http://visualstudiogallery.msdn.microsoft.com/97bd5118-0c29-41b4-9477-d34192c183c3?SRC=VSIDE
- DB2单个DB重启
db2单个数据库重启 . -------------------------------------------------------------- db2 connect to bpm user ...
- Java实现文件拷贝的4种方法.
原文地址:http://blog.csdn.net/ta8210/article/details/2073817 使用 java 进行文件拷贝 相信很多人都会用,,不过效率上是否最好呢? 最近看了看N ...
- shell入门之流程控制语句 分类: 学习笔记 linux ubuntu 2015-07-10 16:38 89人阅读 评论(0) 收藏
1.case 脚本: #!/bin/bash #a test about case case $1 in "lenve") echo "input lenve" ...
- JS的事件监听机制
很久以前有个叫Netscape的姑娘,她制订了Javascript的一套事件驱动机制(即事件捕获) 后来又有一个叫“IE”的小子,这孩子比较傲气,他认为“凭什么我要依照你的规则走”,于是他又创造了一套 ...
- 第一篇:python高级之函数
python高级之函数 python高级之函数 本节内容 函数的介绍 函数的创建 函数参数及返回值 LEGB作用域 特殊函数 函数式编程 1.函数的介绍 为什么要有函数?因为在平时写代码时,如果没 ...
- Asp.net Mvc4 基于Authorize实现的模块访问权限
在MVC中,我们可以通过在action或者controller上设置Authorize[Role="xxx"] 的方式来设置用户对action的访问权限.显然,这样并不能满足我们的 ...
- sql 作业+游标 自动备份数据库
前言 昨天有个同事在客户的服务器上面弄数据库,不小心执行了一条 sql 语句 TRUNCATE TABLE xxx 碉堡了吧,数据全没了 - - ,然后就是在网上拼命的搜索关于数据恢复的软件,搞了一 ...
- ashx页面 “检测到有潜在危险的 Request.Form 值”的解决方法(控制单个处理程序不检测html标签)
如题: 使用web.config的configuration/location节点. 在configuration节点内新建一个location节点,注意这个节点和system.webserver那些 ...
- 进程识别号(PID)的理解
PID(Process Identification)操作系统里指进程识别号,也就是进程标识符.操作系统里每打开一个程序都会创建一个进程ID,即PID. PID(进程控制符)英文全称为Process ...