题目链接: 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. iOS开发总结-UIWebView 集成 浏览器

    // // detailWebViewController.m // BJ // // Created by shirenfeng on 16/11/6. // Copyright © 2016年 c ...

  2. 提高你30%的设计效率的PPT快捷键

    在编辑幻灯片的状态下: [Ctrl]+[A]选择全部对象或幻灯片 [Ctrl]+[B]应用(解除)文本加粗 [Ctrl]+[C]复制 [Ctrl]+[D]快速复制对象 [Ctrl]+[E]段落居中对齐 ...

  3. myeclipse6.0下载及注冊码

    myeclipse6.0 下载地址.官方下载地址: http://www.myeclipseide.com/module-htmlpages-display-pid-4.html 本地快速下载地址: ...

  4. ssss

    18:15 2013/6/18 未结束的事情1 应用API接口切换2 03in.com 项目管理授权 软件中不成功3 党校考核时间 段列出所有 10:51 2013/6/20 daili 应用修改1 ...

  5. [Redux] Navigating with React Router <Link>

    We will learn how to change the address bar using a component from React Router. In Root.js: We need ...

  6. 熟练掌握HDFS的Java API接口访问

    HDFS设计的主要目的是对海量数据进行存储,也就是说在其上能够存储很大量文件(可以存储TB级的文件).HDFS将这些文件分割之后,存储在不同的DataNode上, HDFS 提供了两种访问接口:She ...

  7. 观察者模式在ng(Angular)中的应用

    在这个前端框架满天飞的天下,angular MVVM 的模式确实火了一把,所以最近一直在学习ng,感悟颇多,填坑无数,今天终静下心来打算更新自己久未变动的博客,做一做总结. 1.在ng中的观察者模式: ...

  8. 《3D数学基础:图形与游戏开发》勘误

    最近读这本书,读到四元素的乘法时,自己推导了一下公式,然后懵了,多方查阅,确定是书籍中的笔误(我读的是中文翻译版): 具体在Page147: 上图所示,在第二处明显与第一处的公式不一样. 在维基百科上 ...

  9. SpringSecurity 在MVC 中的简单使用(翻译的,稍加改动)

    Spring Security允许开发人员轻松地将安全功能集成到J2EE Web应用程序中,它通过Servlet过滤器实现“用户自定义”安全检查. 在本教程中,我们将向您展示如何在Spring MVC ...

  10. Axiom3D学习日记 2.介绍SceneManager,SceneNode,Entity

    SceneManager(场景管理类) 所有出现在屏幕里的东西都受SceneManager管理(最好是这样),当你放置对象在场景里,SceneManager就会跟踪他们的位置,当你为场景创建一个相机, ...