https://ac.nowcoder.com/acm/contest/5672/Cios
給出一顆n個點的樹,初始點權爲0,執行m次操做ide
1 x w:給點x的點權加w,其他全部點點權加w-dis(i,x)spa
2 x:將點x的點權和0取mincode
3 x:查詢x的點權blog
對於操做1來講,必需要尋找一種方式,將對全部點的修改進行統一get
樹上兩點間距離很容易想到dis(x,i)=dep(x)+dep(i)-dep(lca)*2,dep表示深度string
則w-dis(x,i)=w-dep(x)-dep(i)+dep(lca)*2it
其中io
w-dep(x)對於全部的點都是同樣的,用一個變量記錄一下便可asm
dep(i)是不會改變的,用一個變量記錄操做1的次數便可
只有dep(lca)比較難處理
假設對如圖所示進行操做1 10 5
紅色的數表示每一個點實際要加的值
藍色的數表示dep(lca)
差分的思想,將每一個點與它的父節點作差
那就只有從根節點到10號點的路徑上,每一個點要進行1的修改
dep(lca)*2,就是每一個點要進行2的修改
至關於在根節點到10號點的路徑上加一個公差爲2的等差數列
具體查詢某個點的dep(lca)時,查詢根節點到這個點的路徑上全部點的和便可
即進行的修改至關於差分,而後前綴和求值
給樹上的某條路徑上全部點加一個值,以及求樹上路徑點權和,這兩種操做樹鏈剖分以後線段樹便可
對於操做2,查詢出該點的點權以後,若是點權>0,記下差值便可
對於操做3,用a表示 Σ(w-dep(x)),b表示操做1的次數,del表示操做2的差值,s表示查詢結果
答案就是a-b*dep+s-del
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 50001 typedef long long LL; int n; int front[N],to[N<<1],nxt[N<<1],tot; int fa[N],dep[N],siz[N]; int id[N],bl[N],dy[N]; LL del[N]; LL sum[N<<2],flag[N<<2]; LL s; void add(int u,int v) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; } void dfs1(int x) { siz[x]=1; for(int i=front[x];i;i=nxt[i]) if(to[i]!=fa[x]) { fa[to[i]]=x; dep[to[i]]=dep[x]+1; dfs1(to[i]); siz[x]+=siz[to[i]]; } } void dfs2(int x,int top) { id[x]=++tot; dy[tot]=x; bl[x]=top; int m=0; for(int i=front[x];i;i=nxt[i]) if(to[i]!=fa[x] && siz[to[i]]>siz[m]) m=to[i]; if(!m) return; dfs2(m,top); for(int i=front[x];i;i=nxt[i]) if(to[i]!=fa[x] && to[i]!=m) dfs2(to[i],to[i]); } void down(int k,int l,int mid,int r) { sum[k<<1]+=(mid-l+1)*flag[k]; sum[k<<1|1]+=(r-mid)*flag[k]; flag[k<<1]+=flag[k]; flag[k<<1|1]+=flag[k]; flag[k]=0; } void change(int k,int l,int r,int opl,int opr) { if(l>=opl && r<=opr) { sum[k]+=r-l+1<<1; flag[k]+=2; return; } int mid=l+r>>1; if(flag[k]) down(k,l,mid,r); if(opl<=mid) change(k<<1,l,mid,opl,opr); if(opr>mid) change(k<<1|1,mid+1,r,opl,opr); sum[k]=sum[k<<1]+sum[k<<1|1]; } void Change(int u) { int v=1; while(bl[u]!=bl[v]) { if(dep[bl[u]]<dep[bl[v]]) swap(u,v); change(1,1,n,id[bl[u]],id[u]); u=fa[bl[u]]; } if(dep[u]>dep[v]) swap(u,v); change(1,1,n,id[u],id[v]); } void query(int k,int l,int r,int opl,int opr) { if(l>=opl && r<=opr) { s+=sum[k]; return; } int mid=l+r>>1; if(flag[k]) down(k,l,mid,r); if(opl<=mid) query(k<<1,l,mid,opl,opr); if(opr>mid) query(k<<1|1,mid+1,r,opl,opr); } void Query(int u) { int v=1; s=0; while(bl[u]!=bl[v]) { if(dep[bl[u]]<dep[bl[v]]) swap(u,v); query(1,1,n,id[bl[u]],id[u]); u=fa[bl[u]]; } if(dep[u]>dep[v]) swap(u,v); query(1,1,n,id[u],id[v]); } int main() { /* int size = 256 << 20; // 256MB char *p = (char*)malloc(size) + size; __asm__("movl %0, %%esp\n" :: "r"(p)); */ // freopen("data.txt","r",stdin); // freopen("my.txt","w",stdout); int T,m,u,v,op; LL a,tmp; int b; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); tot=0; memset(front,0,sizeof(front)); for(int i=1;i<n;++i) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dep[1]=1; dfs1(1); tot=0; dfs2(1,0); a=b=0; memset(sum,0,sizeof(sum)); memset(flag,0,sizeof(flag)); memset(del,0,sizeof(del)); while(m--) { scanf("%d",&op); if(op==1) { scanf("%d%d",&u,&v); a+=v-dep[u]; b++; Change(u); } else { scanf("%d",&u); Query(u); tmp=a-1ll*b*dep[u]+s-del[u]; if(op==2) { if(tmp>0) del[u]+=tmp; } else cout<<tmp<<'\n'; } } } }
太晚了,明天再補