题意:

给出一棵树,root=1,树有点权,有一个人叫做M

有3种操作:

1 u v 把u到v路径上的所有点的点权都给M

2 u 若u的点权在M手上,拿走

3 u 把u为根的子树的所有点权都给M

每一个操作过后,输出M拥有的点权

想法:

要维护路径,用树链剖分

要维护子树,用dfs序

但是这样貌似要写很多

然而后来知道

树链剖分是有dfs序的,也就是说,树链剖分后,对于一个点,其子树所有点的新编号刚好在该点新编号后面的一个连续的区间里面,这个区间的范围[chg[u],chg[u]+siz[u]-1],

这样的话就方便了,我们只需要一个树链剖分就可以了。

树链剖分后用线段树维护,

线段树维护3个值:

all:若区间都被M拥有了,all为1,否则为0

val:在该区间内,M拥有的所有点权的和

sum:在该区间内,所有点的点权的和

(sum在build后就是固定的,不需要再更改)

我们只需要update2个:all和val

这样每一次update后,答案就是seg[1].val了

其实刚开始的时候我只维护2个值:all和sum

然后每一次update操作后我就query一次求出val,然而这样肯定超时啊

加了个val后query函数就直接省略了

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> #define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1 using namespace std; const int maxn=1e5+;
const int inf=0x3f3f3f3f; struct Seg
{
int all,val,sum;
};
Seg seg[maxn<<]; int a[maxn];
int dep[maxn];
int siz[maxn];
int son[maxn];
int fa[maxn];
int top[maxn];
int chg[maxn];
int rev[maxn]; struct Edge
{
int to,next;
};
Edge edge[maxn<<];
int head[maxn];
int tot; void init()
{
memset(head,-,sizeof head);
tot=;
} void addedge(int u,int v)
{
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
} void solve(int ); int main()
{
int test;
scanf("%d",&test);
while(test--){
int n;
scanf("%d",&n);
init();
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
solve(n);
}
return ;
} void dfs0(int u,int pre)
{
fa[u]=pre;
siz[u]=;
dep[u]=dep[pre]+;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
if(v==pre)
continue;
dfs0(v,u);
siz[u]+=siz[v];
if(son[u]==- || siz[v]>siz[son[u]])
son[u]=v;
}
} void dfs1(int u,int tp)
{
top[u]=tp;
chg[u]=++tot;
rev[tot]=u;
if(son[u]==-)
return ;
dfs1(son[u],tp);
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
if(v==fa[u] || v==son[u])
continue;
dfs1(v,v);
}
} void pushup(int rt)
{
seg[rt].val=seg[rt<<].val+seg[rt<<|].val;
} void build(int l,int r,int rt)
{
seg[rt].sum=seg[rt].all=seg[rt].val=;
if(l==r){
seg[rt].sum=a[rev[l]];
return ;
}
int m=(l+r)>>;
build(lson);
build(rson);
seg[rt].sum=seg[rt<<].sum+seg[rt<<|].sum;
} void pushdown(int rt)
{
if(seg[rt].all){
seg[rt<<].all=seg[rt<<|].all=;
seg[rt<<].val=seg[rt<<].sum;
seg[rt<<|].val=seg[rt<<|].sum;
seg[rt].all=;
}
} void update_seg(int L,int R,int add,int l,int r,int rt)
{
if(L<=l&&R>=r){
seg[rt].all=add;
if(add)
seg[rt].val=seg[rt].sum;
else
seg[rt].val=;
return ;
}
int m=(l+r)>>;
pushdown(rt);
if(L<=m)
update_seg(L,R,add,lson);
if(R>m)
update_seg(L,R,add,rson);
pushup(rt);
} void update_path(int u,int v)
{
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])
swap(u,v);
update_seg(chg[top[u]],chg[u],,,tot,);
u=fa[top[u]];
}
if(dep[u]<dep[v])
swap(u,v);
update_seg(chg[v],chg[u],,,tot,);
} void solve(int n)
{
memset(son,-,sizeof son);
memset(dep,,sizeof dep);
dfs0(,);
tot=;
dfs1(,); build(,tot,);
int q;
scanf("%d",&q);
for(int i=;i<=q;i++){
int op;
scanf("%d",&op);
if(op==){
int u,v;
scanf("%d %d",&u,&v);
update_path(u,v);
}
else{
int u;
scanf("%d",&u);
if(op==){
update_seg(chg[u],chg[u],,,tot,);
}
else{
update_seg(chg[u],chg[u]+siz[u]-,,,tot,);
}
}
printf("%d\n",seg[].val);
}
return ;
}

HDU5221 Occupation 树链剖分的更多相关文章

  1. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...

  2. BZOJ 1984: 月下“毛景树” [树链剖分 边权]

    1984: 月下“毛景树” Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 1728  Solved: 531[Submit][Status][Discu ...

  3. codevs 1228 苹果树 树链剖分讲解

    题目:codevs 1228 苹果树 链接:http://codevs.cn/problem/1228/ 看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了. 树链剖分的功能:快速修改,查询树上 ...

  4. 并查集+树链剖分+线段树 HDOJ 5458 Stability(稳定性)

    题目链接 题意: 有n个点m条边的无向图,有环还有重边,a到b的稳定性的定义是有多少条边,单独删去会使a和b不连通.有两种操作: 1. 删去a到b的一条边 2. 询问a到b的稳定性 思路: 首先删边考 ...

  5. 树链剖分+线段树 CF 593D Happy Tree Party(快乐树聚会)

    题目链接 题意: 有n个点的一棵树,两种操作: 1. a到b的路径上,给一个y,对于路径上每一条边,进行操作,问最后的y: 2. 修改某个条边p的值为c 思路: 链上操作的问题,想树链剖分和LCT,对 ...

  6. 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)

    题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...

  7. bzoj2243树链剖分+染色段数

    终于做了一道不是一眼出思路的代码题(⊙o⊙) 之前没有接触过这种关于染色段数的题目(其实上课好像讲过),于是百度了一下(现在思维能力好弱) 实际上每一段有用的信息就是总共有几段和两段各是什么颜色,在开 ...

  8. bzoj3631树链剖分

    虽然是水题1A的感觉太爽了O(∩_∩)O~ 题意相当于n-1次树上路径上每个点权值+1,最后问每个点的权值 本来想写线段树,写好了change打算框架打完了再来补,结果打完发现只是区间加和单点查 前缀 ...

  9. BZOJ 3531: [Sdoi2014]旅行 [树链剖分]

    3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1685  Solved: 751[Submit][Status] ...

随机推荐

  1. 课堂所讲整理:HTML--6运算符、类型转换

    1.类型转换: 分为自动转换和强制转换,一般用强制转换. 其他类型转换为整数:parseint(): 其他类型转换为小数:parsefloat(): 判断是否是一个合法的数字类型:isNaN(): 是 ...

  2. STM32学习笔记:系统时钟和SysTick定时器

    原文:http://blog.sina.com.cn/s/blog_49cb42490100s60d.html 1.     STM32的时钟系统 在STM32中,一共有5个时钟源,分别是HSI.HS ...

  3. Linux平台下利用系统接口函数按照行读写文件

    要求:支持大文件(1M)一次性读入 源代码如下: #include<stdio.h> #include<fcntl.h> #include<stdlib.h> #i ...

  4. Ubuntu 安装和配置redis数据库

    Ubuntu 14.04下安装和配置redis数据库 小编现在在写一个分布式爬虫,要用到这个数据库,所以分享一下小编是如何安装和配置的,希望对大家有帮助. 工具/原料   Ubuntu 系统电脑一台 ...

  5. C# 大于屏幕的窗体

    1.使用SetWindowPos就可以做到这一点,只是最后一个参数要选对. RECT windowRect = new RECT(); User32.GetWindowRect(MyForm2.Han ...

  6. C语言计算开方

    C语言里面有sqrt可以计算开平方根,但似乎想要计算开任意次方根的话却没有一个固定的函数,自己写算法也蛮啰嗦的…… 其实啊,巧妙使用pow函数就可以实现需求. C语言库函数pow的原型声明如下: #i ...

  7. RSA非对称加密 php的openssl实现

    <?php /** * 使用openssl实现非对称加密 * @since 2010-07-08 */ class Rsa { /** * private key */ private $_pr ...

  8. 墨刀 手机app原型工具

    https://modao.io 并且墨刀对开放项目永久免费!

  9. Java中的定时器Timer

    java.util.Timer是一个实用工具类,该类用来调度一个线程,使线程可以在将来某一时刻开始执行. Java的Timer类可以调度一个线程运行一次,或定期运行. java.util.TimerT ...

  10. linux下openoffice的安装和启动

    下载openoffice的安装包(注意选择合适的安装包): http://www.openoffice.org/download/archive.html 一.安装openOffice1.使用tar ...