题目链接: 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. 树莓派做AP发射wifi(RTL8188CUS芯片) 分类: shell ubuntu Raspberry Pi 2014-11-29 01:25 822人阅读 评论(0) 收藏

    最近在做一个项目,需要用树莓派作为AP发射wifi,对比cubieboard,树莓派的配置容易得多,而且支持也更多. 较为官方的介绍配置为无线热点的文章莫过于这一篇<RPI-Wireless-H ...

  2. [Redux] Persisting the State to the Local Storage

    We will learn how to use store.subscribe() to efficiently persist some of the app’s state to localSt ...

  3. Android 仿PhotoShop调色板应用(一)概述

    版权声明:本文为博主原创文章,未经博主允许不得转载. 在前面的系列我已经将Android中颜色渲染的原理及使用做了一个整体上概述. 现在开始根据一个比较复杂的实现进行具体的分析,这就是PhotoSho ...

  4. android 36 线程通信

    安卓中一个程序跑起来叫进程,进程中至少有一个主线程.主线程用于处理用户的触摸操作和将触摸操作事件分发给响应的控件.如果进行消耗时间操作,下载,磁盘读取文件,不润许在主线程操作,只能在工作线程操作.主线 ...

  5. 基于HTML5的SLG游戏开发(一):搭建开发环境(2)

          游戏开发过程中经常需要处理各种事件,而HTML5游戏开发中,所有的场景和UI面板都是绘制在Canvas上面,假设需要对某一UI面板上的关闭按钮添加事件监听,采取的方法是对关闭按钮图片资源进 ...

  6. myeclipse配置svn亲测

    1.安装目录更改为myeclipse install 目录:E:\MyEclipse85\MyEclipse 8.5common 目录:           E:\MyEclipse85\Common ...

  7. poj 3565 ants

    /* poj 3565 递归分治 还有用KM的做法 这里写的分治 按紫书上的方法 不过那里说的有点冗杂了 可以简化一下 首先为啥可以分治 也就是分成子问题解决 只要有一个集合 黑白的个数相等 就一定能 ...

  8. java中XMLGregorianCalendar类型和Date类型之间的相互转换

    import java.text.SimpleDateFormat;import java.util.Date;import java.util.GregorianCalendar;import ja ...

  9. 简单讲解iOS应用开发中的MD5加密的相关使用<转>

    这篇文章主要介绍了iOS应用开发中的MD5加密的相关使用,示例代码基于传统的Objective-C,需要的朋友可以参考下 一.简单说明 1.说明 在开发应用的时候,数据的安全性至关重要,而仅仅用POS ...

  10. Xaml中的资源(1 样式)

    <Window x:Class="MyResoures.MainWindow" xmlns="http://schemas.microsoft.com/winfx/ ...