C题 A National Pandemic
题意:一棵树,n个点,m个操作,操作有三种。
操作1,给出x和w,将所有的点y的权值加上w-dis(x,y)。
操作2,给出x,将x与0取min。
操作3,给出x,求x的权值。
n,m<=40000.
思路:难点就是操作1。
w-dis(x,y)=w-dis(1,x)-dis(1,y)+2 * dis(1,lca(x,y));
dis(1,lca(x,y))的处理是难点,其余直接弄一个变量计数即可。
问题化为:给出x点,对于每个点y的权值加上dis(1,lca(x,y)),然后询问某个点的权值。
解决方法:将x点到1点的路径上的每条边+1,查询某个点权值时查询这个点到点1的路径和即可。(重点!技巧!)
扩展:如果这个题中每条边的权值不相等且wi<=1e9,该如何解决?
如果边权不相等的话,影响的是线段树的区间修改那部分,这样可以在线段树中维护一个a[i].val代表[a[i].l,a[i].r]这一块中的边权权值和,然后当lazy标记下传的时候a[k].sum+=a[k].val*lazy即可。
#include
#include
#include
using namespace std;
const int MAX_N=50100;
int size[MAX_N],wson[MAX_N],dfn[MAX_N],deep[MAX_N],pre[MAX_N],top[MAX_N],fa[MAX_N],dis[MAX_N];
int head[MAX_N],Next[2*MAX_N],ver[2*MAX_N];
int tot,cnt;
struct node{
int l,r,sum,lazy;
}a[MAX_N*4];
int b[MAX_N];
void update(int k){
a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
}
void build(int k,int l,int r){
a[k].l=l;a[k].r=r;a[k].lazy=0;
if(l==r){
a[k].sum=b[pre[l]];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
void pushdown(int k){
if(a[k].lazy==0)
return;
a[k<<1].lazy+=a[k].lazy;
a[k<<1|1].lazy+=a[k].lazy;
a[k<<1].sum+=(a[k<<1].r-a[k<<1].l+1)*a[k].lazy;
a[k<<1|1].sum+=(a[k<<1|1].r-a[k<<1|1].l+1)*a[k].lazy;
a[k].lazy=0;
}
void changeS(int k,int l,int r,int y){
if(a[k].l>=l&&a[k].r<=r){
a[k].sum+=(a[k].r-a[k].l+1)*y;
a[k].lazy+=y;
return;
}
pushdown(k);
int mid=(a[k].l+a[k].r)>>1;
if(l<=mid)
changeS(k<<1,l,r,y);
if(r>mid)
changeS(k<<1|1,l,r,y);
update(k);
}
int query(int k,int l,int r){
if(a[k].l>=l&&a[k].r<=r)
return a[k].sum;
pushdown(k);
int mid=(a[k].l+a[k].r)>>1;
int x=0;
if(l<=mid)
x+=query(k<<1,l,r);
if(r>mid)
x+=query(k<<1|1,l,r);
return x;
}
void Add(int x,int y){
ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
}
void dfs1(int x,int fat){
size[x]=1;
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(y==fat)
continue;
deep[y]=deep[x]+1;
dis[y]=dis[x]+1;
fa[y]=x;
dfs1(y,x);
size[x]+=size[y];
if(size[wson[x]]<size[y])
wson[x]=y;
}
}
void dfs2(int x,int tp){
dfn[x]=++cnt;
pre[cnt]=x;
top[x]=tp;
if(wson[x])
dfs2(wson[x],tp);
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(y!=wson[x]&&y!=fa[x])
dfs2(y,y);
}
}
void modify(int x,int y,int d){//修改x到y路径上的值
while(top[x]!=top[y]){
if(dfn[top[x]]<dfn[top[y]])
swap(x,y);
changeS(1,dfn[top[x]],dfn[x],d);
x=fa[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
changeS(1,dfn[x],dfn[y],d);
}
int Qsum(int x,int y){
int ret=0;
while(top[x]!=top[y]){
if(dfn[top[x]]<dfn[top[y]])
swap(x,y);
ret+=query(1,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
ret+=query(1,dfn[x],dfn[y]);
return ret;
}
int val[MAX_N];
int main(void){
int T,n,m,i,op,x,y,w;
cin>>T;
while(T--){
for(i=1;i<=n;i++){
head[i]=0;
deep[i]=0;
wson[i]=0;
val[i]=0;
}
tot=0;
cnt=0;
scanf("%d%d",&n,&m);
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
Add(x,y);Add(y,x);
}
deep[1]=1;
dis[1]=0;
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
int num=0;
int res=0;
for(i=1;i<=m;i++){
scanf("%d",&op);
if(op==1){
scanf("%d%d",&x,&w);
modify(1,x,2);
res+=w-dis[x];
num++;
}
else if(op==2){
scanf("%d",&x);
int ans=Qsum(1,x)-2*num+res-dis[x]*num+val[x];
if(ans>0)
val[x]-=ans;
}
else if(op==3){
scanf("%d",&x);
int ans=Qsum(1,x)-2*num+res-dis[x]*num+val[x];
printf("%d\n",ans);
}
}
}
return 0;
}