zoukankan      html  css  js  c++  java
  • [BZOJ

    题目链接: 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
    ");
                else printf("No
    ");
            }
        }
        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
    ");
                else printf("No
    ");
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    c# 并行运算二
    c# 并行运算
    Task+http请求
    Task多线程
    SSO系统认证
    web系统权限设计
    AutoMapper的使用
    中间件
    express-middleware
    中间件概念
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4227060.html
Copyright © 2011-2022 走看看