码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 3319: 黑白树

时间:2015-01-09 16:50:23      阅读:271      评论:0      收藏:0      [点我收藏+]

标签:

【题意】

给定一棵树,边的颜色为黑或白,初始时全部为白色。维护两个操作:
 
 
1.查询u到根路径上的第一条黑色边的标号。
2.将u到v    路径上的所有边的颜色设为黑色。
 
Notice:这棵树的根节点为1

 

【题解】

10^6 的数据范围, 树剖是过不了的。

更好的方法?

首先很容易想到 每条边只有 第一次 被染色的时候是有效的, 故可参考 bzoj 2054: 疯狂的馒头 一题用并查集维护向上 第一个白点。 这样每个点保证只会被染一次, 复杂度 是 O(n * k) 的。

然后呢?

我想到一种做法是 dist[i]表示 在 i 时刻之前, 当前点向上最近一条黑边的深度。按照 dfs 的顺序更新,每访问到一个点, 把大于等于(这个点与它父亲的连边 被染黑的时刻)的时刻 的 dist 都改成 这个点与它父亲的连边的深度, 嗯感觉很靠谱,区间修改开一棵可持久化线段树就好了。

orz Towerlight 告诉我 它的 dist 显然是单调的, 所以 随便用单调栈搞搞就好啦, 我说单调栈怎么可持久化啊, 他说这不用 可持久化 只要可以回溯就可以了啊, 我说那怎么 可回溯化啊 , 他说你就就是弱啊,  连 [Noi2014]购票 都没写过.   后来我想了想好像的确是可以搞的, 写单调栈的时候并不把栈中的元素删掉, 而是开个东西指过去,  再把原来指向的地方 保存下来就行了!!

嗯这种方法应该是可以的。

 

下面说一下标解的优美做法:

其实像 预处理时的那样用并查集倒着扫过去一遍就够啦! 预处理的时候是把一棵白树染黑, 这时候把一棵黑树染白就可以了。fat2维护当前点 向上第一个黑点, 按照时间倒着扫, 把 first(第一次变黑的时刻)等于当前时间的点 染白, 然后 询问就是 find(x) 了,,就这么简单,,

 

实现也很容易:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define MAXN 1000005
using namespace std;
int n, m, head[MAXN], change[MAXN], fat[MAXN], ee, deep[MAXN], numm[MAXN], fat2[MAXN], first[MAXN], la[MAXN], cnt[MAXN];
struct Edge{int to, next, num;}edge[MAXN * 2];
inline void addedge(int x, int y, int id){edge[++ ee].to = y; edge[ee].next = head[x]; head[x] = ee; edge[ee].num = id;}
void dfs(int x, int fatt){
    fat[x] = fatt;     
    deep[x] = deep[fatt] + 1;
    for(int i = head[x]; i != -1; i = edge[i].next)if(edge[i].to != fatt)
        numm[edge[i].to] = edge[i].num, dfs(edge[i].to, x);
}
inline int get(int x){return x == fat2[x] ? x : fat2[x] = get(fat2[x]);}
void color (int x, int y, int tt){
    x = get(x), y = get(y);    
    while(x != y){
        if(deep[x] < deep[y])swap(x, y);
        if(first[x] != m + 1)x = fat2[x];
        else fat2[x] = fat2[fat[x]], first[x] = tt, x = fat[x];
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    memset(head, -1, sizeof(head));
    memset(la, -1, sizeof(la));
    for(int i = 1; i < n; i ++){
        int x, y; scanf("%d%d", &x, &y);
        addedge(x, y, i); addedge(y, x, i);    
    }
    for(int i = 1; i <= n; i ++)fat2[i] = i, first[i] = m + 1;
    dfs(1, 1);
    for(int i = 1; i <= m; i ++){
        int x, y; scanf("%d", &x);
        if(x == 2){
            scanf("%d%d", &x, &y);
            color (x, y, i);
        }else scanf("%d", &la[i]);
    }
    for(int i = 1; i <= n; i ++) ++ cnt[first[i]];
    for(int i = 1; i <= m + 1; i ++)cnt[i] += cnt[i - 1];
    for(int i = 1; i <= n; i ++)change[cnt[first[i]] --] = i;
    for(int i = 1; i <= n; i ++)fat2[i] = i;
    int all = n, u;
    for(int i = m + 1; i; i --){
        if(la[i] != -1)la[i] = numm[get(la[i])];
        else for(; all && first[u = change[all]] == i; all --)
                    fat2[u] = fat2[fat[u]];    
    }
    for(int i = 1; i <= m; i ++)
        if(la[i] != -1)printf("%d\n", la[i]);
    return 0;
}

 

bzoj 3319: 黑白树

标签:

原文地址:http://www.cnblogs.com/lixintong911/p/4213526.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!