HDU 5221 Occupation dfs序版树链剖分

题目大意:

就是现在给出一棵树, 以1为根, 树上的点都有自己的权值, 初始都没有被占领, 接下来3种操作

1. 从u到v的路径上的所有点被M占领

2. 某个点u被C占领

3. 以u为根的子树被M占领

每次操作后输出当前被M占领的那些点的点权和


大致思路:

很明显对于操作1和2用树链剖分很容易处理

对于第3种操作, 需要利用树的时间戳...于是这里写了一次dfs版的树链剖分

也没有什么特别了的吧...我线段树维护的时候记录当前区间总和, 和被占领的部分的总和, lazy标记时候被还原亦或被占领

对于第1种操作单次时间复杂度O((logn)^2), 另外两种单次时间复杂度都是O(logn)


代码如下:

Result  :  Accepted     Memory  :  13028 KB     Time  :  1528 ms

/*
 * Author: Gatevin
 * Created Time:  2015/9/10 23:13:50
 * File Name: HLD_dfs.cpp
 */
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 100010
int n;
int w[maxn];

struct Edge
{
    int u, v, nex;
    Edge(int _u, int _v, int _nex)
    {
        u = _u, v = _v, nex = _nex;
    }
    Edge(){}
};

Edge edge[maxn << 1];
int tot;
int head[maxn];

void add_Edge(int u, int v)
{
    edge[++tot] = Edge(u, v, head[u]);
    head[u] = tot;
}

int father[maxn];
int dep[maxn];
int siz[maxn];
int hson[maxn];//u的重儿子为hson[u], 为-1表示没有
int top[maxn];//top[u]表示结点u所在链的顶端结点
int L[maxn], R[maxn];//[L[u], R[u]]代表结点u的dfs序时间戳
int antiID[maxn];//antiID[x]表示树上那个结点在区间上位置是x
//在dfs的方式下, 没有用id[x]表示结点x所在的区间位置, 因为L[]数组代替了这个作用
int tim;//时间戳时间

void dfs1(int now)
{
    siz[now] = 1;
    hson[now] = -1;
    int nex;
    for(int i = head[now]; i + 1; i = edge[i].nex) if((nex = edge[i].v) != father[now])
    {
        father[nex] = now;
        dep[nex] = dep[now] + 1;
        dfs1(nex);
        siz[now] += siz[nex];
        if(hson[now] == -1 || siz[nex] > siz[hson[now]]) hson[now] = nex;
    }
}

void dfs2(int now, int tp)
{
    L[now] = ++tim; 
    top[now] = tp;
    antiID[tim] = now;
    if(hson[now] != -1) dfs2(hson[now], tp);
    int nex;
    for(int i = head[now]; i + 1; i = edge[i].nex)
        if((nex = edge[i].v) != father[now] && nex != hson[now])
            dfs2(nex, nex);
    R[now] = tim;
}

void split()
{
    tim = 0;
    father[1] = -1;
    dep[1] = 1;
    dfs1(1);
    dfs2(1, 1);
}

int ans;

//Segment_Tree
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
int sum[maxn << 2];//当前区间总和
int occupy[maxn << 2];//当前结点代表的区间已经占领的和
int lazy[maxn << 2];//

void pushUp(int rt)
{
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    occupy[rt] = occupy[rt << 1] + occupy[rt << 1 | 1];
}

void pushDown(int rt)
{
    if(lazy[rt] == 1)
    {
        lazy[rt << 1] = lazy[rt << 1 | 1] = 1;
        occupy[rt << 1] = sum[rt << 1];
        occupy[rt << 1 | 1] = sum[rt << 1 | 1];
        lazy[rt] = 0;
    }
    if(lazy[rt] == 2)
    {
        lazy[rt << 1] = lazy[rt << 1 | 1] = 2;
        lazy[rt] = 0;
        occupy[rt << 1] = occupy[rt << 1 | 1] = 0;
    }
}

void build(int l, int r, int rt)
{
    sum[rt] = 0, occupy[rt] = 0, lazy[rt] = 0;
    if(l == r)
    {
        sum[rt] = w[antiID[l]];
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    pushUp(rt);
}

void update(int l, int r, int rt, int L, int R, int state)//state == 1更新为占用, 否则不占用
{
    if(l >= L && r <= R)
    {
        if(state == 1)
        {
            ans += sum[rt] - occupy[rt];
            occupy[rt] = sum[rt];
            lazy[rt] = 1;
        }
        else
        {
            ans -= occupy[rt];
            occupy[rt] = 0;
            lazy[rt] = 2;
        }
        return;
    }
    int mid = (l + r) >> 1;
    pushDown(rt);
    if(mid >= L) update(lson, L, R, state);
    if(mid + 1 <= R) update(rson, L, R, state);
    pushUp(rt);
}

void modify(int x, int y)//更新x到y的路径为M占用
{
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]])
            swap(x, y);
        update(1, n, 1, L[top[x]], L[x], 1);
        x = father[top[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    update(1, n, 1, L[x], L[y], 1);
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        tot = 0;
        memset(head, -1, sizeof(head));
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", w + i);
        int u, v;
        for(int i = 1; i < n; i++)
        {
            scanf("%d %d", &u, &v);
            add_Edge(u, v);
            add_Edge(v, u);
        }
        split();
        build(1, n, 1);
        ans = 0;
        int Q;
        scanf("%d", &Q);
        while(Q--)
        {
            int op;
            scanf("%d", &op);
            switch(op)
            {
                case 1: scanf("%d %d", &u, &v);
                        modify(u, v);//u和v之间的路径被M占领
                        break;
                case 2: scanf("%d", &u);
                        update(1, n, 1, L[u], L[u], 0);//u被C占领
                        break;
                case 3: scanf("%d", &u);
                        update(1, n, 1, L[u], R[u], 1);//M占领u为根的子树
                        break;
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}


你可能感兴趣的:(HDU,occupation,5221,dfs序版树链剖分)