题目描述
给出一棵树,点有点权。多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值。
输入
输入文件第一行包含一个数n,表示树的大小。
接下来一行包含n个数,表示第i个点的权值。
接下来n-1行每行包含两个数fr,to。表示书中有一条边(fr,to)。
接下来一行一个整数,表示操作的个数。
接下来m行每行表示一个操作,若该行第一个数为Q,则表示询问操作,后面跟一个参数x,表示对应子树的根;若
为C,则表示修改操作,后面接两个参数x,to,表示将点x的权值加上to。
n<=200000,保证任意to都为非负数
输出
对于每次询问操作,输出对应的答案,答案之间用换行隔开。
样例输入
4
4 3 2 1
1 2
1 3
4 2
4
Q 1
Q 2
C 4 10
Q 1
样例输出
3
1
4
解析:
在猫锟论文上看见的题,来写一写。
显然每次 O ( n ) O(n) O(n)的DP是很好想且会超时的。
设 f [ u ] f[u] f[u]表示将在 u u u子树中的叶子全部覆盖需要花费的代价, c h ( u ) ch(u) ch(u)是 u u u的儿子集合,则我们有如下转移:
f [ u ] = min ( a [ u ] , ∑ v ∈ c h ( u ) f [ v ] ) f[u]=\min(a[u],\sum_{v\in ch(u)}f[v]) f[u]=min(a[u],v∈ch(u)∑f[v])
考虑将轻重儿子分开,令 C h ( u ) Ch(u) Ch(u)表示 u u u的轻儿子集合, s s s表示 u u u的重儿子。
发现这个玩意已经可以链分治了。
令 g [ u ] = ∑ v ∈ C h ( u ) f [ v ] g[u]=\sum_{v\in Ch(u)}f[v] g[u]=∑v∈Ch(u)f[v],我们直接维护所有点的 g [ u ] , a [ u ] g[u],a[u] g[u],a[u],记为二元组 ( g , a ) (g,a) (g,a)。
现在我们发现每次计算其实只需要从当前点的对应重链底向上一路DP就行了。
然而这个DP其实上只是维护一个前缀和的最小值。。。直接上线段树就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline char getalpha(){
re char c;
while(!isalpha(c=gc()));return c;
}
inline ll getint(){
re char c;
while(!isdigit(c=gc()));re ll num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int N=2e5+5;
int n,m;
int last[N],nxt[N<<1],to[N<<1],ecnt;
inline void addedge(int u,int v){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}
int fa[N],bot[N],top[N],siz[N],son[N];
int in[N],pos[N],dfs_clock;
void dfs1(int u){
siz[u]=1;
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])if(v!=fa[u]){
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
ll g[N],f[N],a[N];
void dfs2(int u){
pos[in[u]=++dfs_clock]=u;
bot[u]=u;f[u]=a[u];
if(son[u]){
top[son[u]]=top[u];
dfs2(son[u]);
bot[u]=bot[son[u]];
}
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])if(v!=fa[u]&&v!=son[u]){
top[v]=v;
dfs2(v);
g[u]+=f[v];
}
if(son[u])f[u]=std::min(f[u],g[u]+f[son[u]]);
}
struct data{
ll sum,ls;
data(){}
data(cs ll &_sum,cs ll &_ls):sum(_sum),ls(_ls){}
friend data operator+(cs data &a,cs data &b){
return data(a.sum+b.sum,std::min(a.sum+b.ls,a.ls));
}
}val[N<<2];
inline void pushup(int k){val[k]=val[k<<1]+val[k<<1|1];}
inline void build(int k,int l,int r){
if(l==r){
val[k]=data(g[pos[l]],a[pos[l]]);
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
inline void updatev(int k,int l,int r,cs int &pos,cs ll &delta){
if(l==r){
val[k].ls+=delta;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)updatev(k<<1,l,mid,pos,delta);
else updatev(k<<1|1,mid+1,r,pos,delta);
pushup(k);
}
inline void updateg(int k,int l,int r,cs int &pos,cs ll &delta){
if(l==r){
val[k].sum+=delta;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)updateg(k<<1,l,mid,pos,delta);
else updateg(k<<1|1,mid+1,r,pos,delta);
pushup(k);
}
inline data query(int k,int l,int r,cs int &ql,cs int &qr){
if(ql<=l&&r<=qr)return val[k];
int mid=(l+r)>>1;
if(qr<=mid)return query(k<<1,l,mid,ql,qr);
if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
}
inline void update(int u,ll vl){
int v=u;
ll t;
while(u){
if(vl==0)return ;
t=query(1,1,n,in[top[u]],in[bot[u]]).ls;
if(v==u)updatev(1,1,n,in[u],vl);
else updateg(1,1,n,in[u],vl);
vl=query(1,1,n,in[top[u]],in[bot[u]]).ls-t;
u=fa[top[u]];
}
}
signed main(){
n=getint();
for(int re i=1;i<=n;++i)a[i]=getint();
for(int re i=1;i<n;++i)addedge(getint(),getint());
dfs1(1),top[1]=1,dfs2(1);
build(1,1,n);
m=getint();
while(m--)switch(getalpha()){
case 'Q':{
int u=getint();
cout<<query(1,1,n,in[u],in[bot[u]]).ls<<"\n";
break;
}
case 'C':{
int u=getint();ll val=getint();
update(u,val);
break;
}
}
return 0;
}