树链剖分 - hdu5221 Occupation

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5221


题意:

给定一棵树,每个结点上有一个权值,然后给Q个操作,每个操作为:1. 占领这个树上两个结点路径上的所有结点,包括起点和终点;2. 取消某个结点的占领; 3. 占领以某个结点为根的整棵子树; 对于每个操作,返回完成这个操作后整棵树被占领结点的权值之和


思路:

操作1和2是树链剖分的基础操作不详述,操作3用到了树链剖分的一个特性,以结点k为根的子树,树链剖分完在线段上是一段连续的序列,在树链剖分的第二次dfs过程中用sontree数组顺便记录以j为根的子树,其在线段中对应的最大一个id值,把这个操作转换为线段树上的区间更新操作

在操作线段树时,区间更新需要打懒标记,不然会超时


代码:

#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<functional>
#pragma comment(linker, "/STACK:102400000,102400000")//C++
using namespace std;

const double PI = 3.141592653589793238462643383279502884197169399;
const int MAXINT = 0x7fffffff;
const int MAXSIZE = 100000 + 5;

int n,q;
int fa[MAXSIZE], siz[MAXSIZE], son[MAXSIZE], dep[MAXSIZE], top[MAXSIZE], tid[MAXSIZE], sontree[MAXSIZE], ptid[MAXSIZE], w[MAXSIZE];
int first[MAXSIZE], gnext[2*MAXSIZE], to[2*MAXSIZE], edge;
int tsum;
int ans;

void dfs_1(int v,int father,int depth){
    siz[v] = 1;
    dep[v] = depth;
    fa[v] = father;
    son[v] = 0;
    int i=first[v];
    while (i){
        int u = to[i];
        if (u!=father){
            dfs_1(u,v,depth+1);
            siz[v]+=siz[u];
            if (siz[u]>siz[son[v]]) son[v] = u;
        }
        i = gnext[i];
    }
    return;
}

void dfs_2(int v, int tp){
    tid[v] = ++tsum;
    ptid[tsum] = v;
    top[v] = tp;
    sontree[v] = tid[v];
    if (son[v] != 0){
        dfs_2(son[v], top[v]);  //使重链在线段上连续
        sontree[v] = sontree[son[v]];
    }
    int i=first[v];
    while (i){
        int u = to[i];
        if (u!=fa[v] && u!=son[v]){
            dfs_2(u, u);
            if (sontree[v] < sontree[u]) sontree[v] = sontree[u];
        }
        i = gnext[i];
    }
    return ;
}

struct node{
	int l, r;
	int sum, wsum;
	bool all;
	int mid(){
		return (l + r) / 2;
	}
};
node tree[(MAXSIZE << 2) + 5];

//初始状态 BuildTree(0,1,n)
void BuildTree(int root, int l, int r){
	tree[root].l = l; tree[root].r = r; tree[root].sum=0; tree[root].all = false;
	if (l != r){
		BuildTree(root * 2 + 1, l, (l + r) / 2);
		BuildTree(root * 2 + 2, (l + r) / 2 + 1, r);
        tree[root].wsum = tree[(root<<1)+1].wsum + tree[(root<<1)+2].wsum;
	}
	else{
        tree[root].wsum = w[ptid[l]];
	}
	return;
}

//更新节点 (0,i,w)
void t_insert(int root, int i){
	if (tree[root].l == tree[root].r){
        tree[root].all = false;
        tree[root].sum = 0;
        return;
	}
	else{
        if (tree[root].all){
            tree[root].all = false;
            tree[(root<<1) + 2].all = true;
            tree[(root<<1) + 2].sum = tree[(root<<1) + 2].wsum;
            tree[(root<<1) + 1].all = true;
            tree[(root<<1) + 1].sum = tree[(root<<1) + 1].wsum;
        }
        if (i <= tree[root].mid())
            t_insert(root * 2 + 1, i);
        else
            t_insert(root * 2 + 2, i);

        tree[root].sum = tree[(root<<1)+1].sum + tree[(root<<1)+2].sum;
	}
}

//更新线段[st,en] (0,st,en,w)
void t_update(int root, int st, int en){
	if (tree[root].all) return;

	if (tree[root].l == st && tree[root].r == en){
		tree[root].all = true;
		tree[root].sum = tree[root].wsum;
        return;
	}
	if (st > tree[root].mid())
		t_update(root * 2 + 2, st, en);
	else if (en <= tree[root].mid())
		t_update(root * 2 + 1, st, en);
	else {
		t_update(root * 2 + 1, st, tree[root].mid());
		t_update(root * 2 + 2, tree[root].mid() + 1, en);
	}
	tree[root].sum = tree[(root<<1) + 1].sum + tree[(root<<1) + 2].sum;
	return;
}

void g_insert(int a, int b){
    gnext[++edge] = first[a];
    first[a] = edge;
    to[edge] = b;
}

void init(){
    memset(first,0,sizeof(first));
    memset(siz,0,sizeof(siz));
    memset(son,0,sizeof(son));
    memset(dep,0,sizeof(dep));
    memset(fa,0,sizeof(fa));
    memset(top,0,sizeof(top));
    memset(tid,0,sizeof(tid));
    memset(sontree,0,sizeof(sontree));
    memset(ptid,0,sizeof(ptid));
    memset(w,0,sizeof(w));
    tsum = 0;
    edge=0;
}

int main(){
    int total;
    cin>>total;
    while (total--){
        init();

        scanf("%d",&n);
        for (int i=1;i<=n;++i)
            scanf("%d",w+i);

        for (int i=1;i<n;++i){
            int a,b,c;
            scanf("%d %d",&a,&b);
            g_insert(a,b);
            g_insert(b,a);
        }

        dfs_1(1,0,0);
        dfs_2(1,1);

//        for (int i=1;i<=n;++i){
//            cout<<"sontree: "<<i<<" "<<tid[i]<<" "<<ptid[sontree[i]]<<" "<<sontree[i]<<endl;
//        }

        BuildTree(0,1,tsum);

        scanf("%d",&q);
        for (int i=1;i<=q;++i){
            int a;
            int u,v;
            scanf("%d",&a);
            if (a==1){
                scanf("%d %d",&u,&v);
                int f1 = top[u],f2 = top[v];
                while (f1 != f2){
                    if (dep[f1] < dep[f2]){
                        swap(f1,f2);
                        swap(u,v);
                    }
                    t_update(0, tid[f1], tid[u]);
                    u=fa[f1];
                    f1=top[u];
                }
                if (u!=v){
                    if (dep[u] > dep[v]) swap(u,v);
                    t_update(0, tid[u], tid[v]);
                }
                else{
                    t_update(0, tid[u], tid[v]);
                }
            }
            else if (a==2){
                scanf("%d",&u);
                t_insert(0,tid[u]);
            }
            else {
                scanf("%d",&u);
                t_update(0,tid[u],sontree[u]);
            }
            ans=tree[0].sum;
            printf("%d\n",ans);
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值