1. 程式人生 > >HDU 5221 Occupation dfs序版樹鏈剖分

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;
}