zoukankan      html  css  js  c++  java
  • [XJOI NOI2015模拟题13] C 白黑树 【线段树合并】

    题目链接:XJOI - NOI2015-13 - C

    题目分析

    使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目。

    对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动态开点。

    线段树的节点维护两个值,一个是这段时间内的 1 操作个数,另一个是这段时间内变化的黑色节点权值和。

    在处理所有操作的时候,每棵线段树都是仅代表树上的一个点,因此线段树的每个节点维护的就是这段时间内以这个点为 a 的 1 操作个数和这段时间内这个点的黑色节点权值和(这个点 x 由黑变白就 -x, 由白变黑就 +x)。

    在处理完所有操作后,我们进行一次 DFS,自底向上将线段树进行合并。

    目前 DFS(x),先递归处理完 x 的每棵子树,然后枚举 x 的每棵子树,依次将它们的线段树合并到 x 的线段树上。

    现在已经将 x 的前 j-1 棵子树的线段树合并到了 x 的线段树上,现在将第 j 棵子树的线段树合并到 x 的线段树上。

    对于处于 j 子树内的 a 和处于 x 点或前 j-1 棵子树内的黑点,它们的 LCA 就是 x 点,因此他们对 x 的权值有贡献。

    同理,处于 j 子树内的黑点和处于 x 点或前 j-1 棵子树内的 a ,他们的 LCA 也是 x 点,也要计算他们对 x 的权值的贡献。

    一个黑点权值修改会对时间 key 比它大的 1 操作产生影响。

    合并时,记合并的两棵线段子树为 (x, y),那么答案就要加上 Son[x][0] 的黑点权值修改 * Son[y][1] 的 1 操作个数。

    同理,答案也要加上 Son[y][0] 的黑点权值修改 * Son[x][1] 的 1 操作个数。

    然后递归下去合并 (Son[x][0], Son[y][0]) ,合并 (Son[x][1], Son[y][1]),继续计算两边子树内部的答案。

    同时注意,这样计算的答案不包括 a 点本身就是一个黑点时贡献的权值,所以要单独加上这个情况的权值。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    inline void Read(int &Num)
    {
    	char c = getchar();
    	bool Neg = false;
    	while (c < '0' || c > '9') 
    	{
    		if (c == '-') Neg = true;
    		c = getchar();
    	}
    	Num = c - '0'; c = getchar();
    	while (c >= '0' && c <= '9')
    	{
    		Num = Num * 10 + c - '0';
    		c = getchar();
    	}
    	if (Neg) Num *= -1;
    }
    
    const int MaxN = 200000 + 5, MaxNode = 7200000 + 5;
    
    int n, m, Index;
    int A[MaxN], Root[MaxN], Son[MaxNode][2], T[MaxNode];
    
    LL Cnt;
    LL Ans[MaxN], Sum[MaxNode];
    
    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 Update(int x)
    {
    	T[x] = T[Son[x][0]] + T[Son[x][1]];
    	Sum[x] = Sum[Son[x][0]] + Sum[Son[x][1]];
    }
    
    void Add(int &x, int s, int t, int Pos, int Ds, int Dt)
    {
    	if (x == 0) x = ++Index;
    	if (s == t)
    	{
    		Sum[x] += (LL)Ds;
    		T[x] += Dt;
    		return;
    	}
    	int m = (s + t) >> 1;
    	if (Pos <= m) Add(Son[x][0], s, m, Pos, Ds, Dt);
    	else Add(Son[x][1], m + 1, t, Pos, Ds, Dt);
    	Update(x);
    }
    
    int Merge(int x, int y, int s, int t)
    {
    	if (!x) return y;
    	if (!y) return x;
    	if (s == t)
    	{
    		T[x] += T[y];
    		Sum[x] += Sum[y];
    		return x;
    	}
    	Cnt += (LL)T[Son[x][1]] * Sum[Son[y][0]];
    	Cnt += (LL)T[Son[y][1]] * Sum[Son[x][0]];
    	int m = (s + t) >> 1;
    	Son[x][0] = Merge(Son[x][0], Son[y][0], s, m);
    	Son[x][1] = Merge(Son[x][1], Son[y][1], m + 1, t);
    	Update(x);
    	return x;
    }
    
    void Solve(int x, int Fa)
    {
    	for (Edge *j = Point[x]; j; j = j -> Next)
    	{
    		if (j -> v == Fa) continue;
    		Solve(j -> v, x);
    	}
    	for (Edge *j = Point[x]; j; j = j -> Next)
    	{
    		if (j -> v == Fa) continue;
    		Cnt = 0;
    		Root[x] = Merge(Root[x], Root[j -> v], 0, m);
    		Ans[x] += Cnt;
    	}
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++i) 
    	{
    		Read(A[i]);
    		if (A[i] != 1) A[i] = 0;
    	}
    	int a, b;
    	for (int i = 1; i < n; ++i) 
    	{
    		Read(a); Read(b);
    		AddEdge(a, b); AddEdge(b, a);
    	}
    	for (int i = 1; i <= n; ++i)
    		Add(Root[i], 0, m, 0, A[i] * i, 0);
    	int f, x;
    	for (int i = 1; i <= m; ++i)
    	{
    		Read(f); Read(x);
    		if (f == 1) 
    		{
    			Add(Root[x], 0, m, i, 0, 1);
    			if (A[x]) Ans[x] += (LL)x;
    		}
    		else
    		{
    			A[x] ^= 1;
    			if (A[x]) Add(Root[x], 0, m, i, x, 0);
    			else Add(Root[x], 0, m, i, -x, 0);
    		}
    	}
    	Solve(1, 0);
    	for (int i = 1; i <= n; ++i) 
    		printf("%lld
    ", Ans[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    json编解码
    Grok 正则捕获
    logstash date插件介绍
    logstash 字段类型转换后 需要刷新
    logstash 防止实际处理时间跟事件产生时间略有偏差
    导入旧数据需要 使用date插件
    nginx和tomcat的响应时间
    解决kibana 4 关于响应时间的问题
    go 可以开发桌面应用
    windows下go语言环境
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4600441.html
Copyright © 2011-2022 走看看