Description

小A走到一个山脚下,准备给自己造一个小屋。这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小A面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个n个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。

Solution

这道题真的是666。。

我们设g[x]为堵住该点所有子树的和,v[x]为堵住该点的代价,则f[x]=min(g[x],v[x])。现在我们要给v[x]加上to。

1,v[x]>=g[x],v[x]加多少都不会有影响,过;

2,v[x]<=g[x]&&v[x]+to<=g[x],则g[fa[x]]+=to,如果f[fa[x]]改变,则还需要接着往上推。

3,v[x]<=g[x]&&v[x]+to>g[x],则g[fa[x]]+=-v[x]+g[x],如果f[fa[x]]改变,同样需要接着往上推。

对于情况1,直接处理掉就ok了。

我们考虑优化情况2和3的处理。在这里我们采用树剖+线段树。线段树存储v[x]-g[x]。

对于点x,我们沿着重链往上,如果在某条重链上都是情况2或3,直接加就好;反之在这条重链上二分。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,x,y;
ll v[],_g[],dp[],d;
struct node{int y,nxt;
}g[];int h[],tot=;
int m;char ch[]; int fa[],dep[],son[],top[],sz[];
int cnt=,dfn[],id[]; struct XD_TREE
{
ll mn[],tag[];
void build(int k,int l,int r)
{
if (l==r) {mn[k]=v[dfn[l]]-_g[dfn[l]];return;}
int mid=(l+r)/;
build(k<<,l,mid);build(k<<|,mid+,r);
mn[k]=min(mn[k<<],mn[k<<|]);
}
void downtag(int k)
{
mn[k<<]-=tag[k];mn[k<<|]-=tag[k];
tag[k<<]+=tag[k];tag[k<<|]+=tag[k];
tag[k]=;
}
int modify(int k,int l,int r,int askl,int askr,ll d)
{
if (l==r){
mn[k]-=d;tag[k]+=d;
if (mn[k]<=) return dfn[l];
return ;
}
if (askl<=l&&r<=askr){
if (mn[k]>d)
{
mn[k]-=d;tag[k]+=d;return ;
}
}
if (tag[k]!=) downtag(k);
int mid=(l+r)/,re=;
if (askr>mid) re=modify(k<<|,mid+,r,askl,askr,d);
if (askl<=mid&&!re) re=modify(k<<,l,mid,askl,askr,d);
mn[k]=min(mn[k<<],mn[k<<|]);
return re;
}
ll query(int k,int l,int r,int ask)
{
if (l==r) return mn[k];
if (tag[k]!=) downtag(k);
int mid=(l+r)/;
if (ask<=mid) return query(k<<,l,mid,ask);
else return query(k<<|,mid+,r,ask);
}
}X;
struct TREE_LINK//树剖
{
void dfs1(int x,int f)
{
fa[x]=f;dep[x]=dep[f]+;sz[x]=;
for(int i=h[x];i;i=g[i].nxt)
if (g[i].y!=f) {
dfs1(g[i].y,x);
sz[x]+=sz[g[i].y];
son[x]=(sz[son[x]]>=sz[g[i].y])?son[x]:g[i].y;
_g[x]+=dp[g[i].y];
}
if (sz[x]>) dp[x]=min(_g[x],v[x]);else dp[x]=v[x],_g[x]=1e9;
}
void dfs2(int x)
{
dfn[++cnt]=x;id[x]=cnt;
top[x]=son[fa[x]]==x?top[fa[x]]:x;
if (son[x]) dfs2(son[x]);
for (int i=h[x];i;i=g[i].nxt)
if (g[i].y!=fa[x]&&g[i].y!=son[x]) dfs2(g[i].y);
}
void work(int x,ll d)
{
if (!x||d<=) return;
while (x)
{
int t=X.modify(,,n,id[top[x]],id[x],d);
if (t) {work(fa[t],X.query(,,n,id[t])+d);break;}
x=fa[top[x]];
}
}
}T;
ll getg(int x){return v[x]-X.query(,,n,id[x]);}
int main()
{
scanf("%d",&n);
for (int i=;i<=n;i++) scanf("%lld",&v[i]);
for (int i=;i<n;i++)
{
scanf("%d%d",&x,&y);
g[++tot]=node{y,h[x]};h[x]=tot;
g[++tot]=node{x,h[y]};h[y]=tot;
}
T.dfs1(,);
T.dfs2();
X.build(,,n);
scanf("%d",&m);
for (int i=;i<=m;i++)
{
scanf("%s",ch);
if (ch[]=='Q')
{
scanf("%d",&x);
ll gg=getg(x);
printf("%lld\n",min(gg,v[x]));
} else
{
scanf("%d%lld",&x,&d);
v[x]+=d;
X.modify(,,n,id[x],id[x],-d);
ll gg=getg(x);
if (v[x]-d>=gg) continue;
if (v[x]<=gg) T.work(fa[x],d);
else T.work(fa[x],gg-v[x]+d);
}
}
}
 

[BZOJ4712]洪水-[树链剖分+线段树]的更多相关文章

  1. 【bzoj4712】洪水 树链剖分+线段树维护树形动态dp

    题目描述 给出一棵树,点有点权.多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值. 输入 输入文件第一行包含 ...

  2. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  3. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  4. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  5. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  6. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  7. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  8. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  9. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

  10. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

随机推荐

  1. 环境变量、block、修饰符:block对环境变量的引用和修改需要通过修饰符来限定

    环境变量.block.修饰符:block对环境变量的引用和修改需要通过修饰符来限定. http://www.cnblogs.com/fengmin/p/5816580.html - (NSUInteg ...

  2. 最大传输单元MTU

    http://baike.baidu.com/link?url=mU41JFjZzOb3R5crQFCNdocT5ovAswcoIqL2A4U6O5Re_U0-HIYndHG0vSKwc6HbptvH ...

  3. POJ3690 Constellations

    嘟嘟嘟 哈希 刚开始我一直在想二维哈希,但发现如果还是按行列枚举的话会破坏子矩阵的性质.也就是说,这个哈希只能维护一维的子区间的哈希值. 所以我就开了个二维数组\(has_{i, j}\)表示原矩阵\ ...

  4. Hadoop学习之路(十八)MapReduce框架Combiner分区

    对combiner的理解 combiner其实属于优化方案,由于带宽限制,应该尽量map和reduce之间的数据传输数量.它在Map端把同一个key的键值对合并在一起并计算,计算规则与reduce一致 ...

  5. 【vue】父向子组件传参、子组件向父传参

    1.父向子组件传参 App.vue为父,引入componetA组件之后,则可以在App.vue中使用标签(注意驼峰写法要改成componet-a写法,因为html对大小写不敏感,componenta与 ...

  6. 编程中关于对时区的理解(语言PHP)

    时间戳 首先来说说time()返回的时间戳,时间戳则是自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的秒数. 格林威治 由于地球圆的,因此每个时区的时间 ...

  7. PAT——1042. 字符统计

    请编写程序,找出一段给定文字中出现最频繁的那个英文字母. 输入格式: 输入在一行中给出一个长度不超过1000的字符串.字符串由ASCII码表中任意可见字符及空格组成,至少包含1个英文字母,以回车结束( ...

  8. PAT——1020. 月饼

    月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼.现给定所有种类月饼的库存量.总售价.以及市场的最大需求量,请你计算可以获得的最大收益是多少. 注意:销售时允许取出一部分库存.样 ...

  9. 学习使用 ARM 的 math 库,据说 速度比C标准库 自带的 快 几十倍 到几百倍

    1.首先 添加 库 到 工程 ,路径 如下 C:\Keil\ARM\CMSIS\Lib\ARM 2.包含头文件以及在 工程里 添加 头文件 路径如下  C:\Keil\ARM\CMSIS\Includ ...

  10. VS2012打开Web项目提示《ASP.NET 4.X 尚未在Web服务器上注册。你需要手动将...》解决方案

    用VS12创建的WEB项目时,默认使用IIS Experess开发服务器,但是每次打开会提示如下提示 但是同一个项目用VS13或VS15打开时,就能正常打开,不会出现以上提示信息. 怀疑是IIS Ex ...