HDU 4918 Query on the subtree(動態點分治+樹狀陣列)
阿新 • • 發佈:2018-12-28
題意
給定一棵 \(n\) 個節點的樹,每個節點有點權。完成 \(q\) 個操作——操作分兩種:修改點 \(x\) 的點權、查詢與 \(x\) 距離小於等於 \(d\) 的權值總和。
\(1 \leq n,q \leq 10^5\)
思路
從最簡單的情況分析——只有一次查詢。當然一遍 \(O(n)\) 的 \(\text{dfs}\) 可以直接寫,不過要用點分治寫的話,\(\text{solve}\) 函式直接容斥一下就可以了。
如果多個詢問呢?其實在回答關於點 \(x\) 的詢問時,其實只需要計算管轄 \(x\) 的所有重心的答案。我們只需要將點分治的過程記錄下來,查詢只查管轄 \(x\)
具體的實現每道題略有區別,但具體思路大致相同。別忘了我們是從一次查詢作優化,那麼我們對於一個點,記錄它到重心的距離;對於每個重心開一個數組,表示管轄範圍內距離為 \(d\) 的節點權值總和,然後字首和一下就變成了距離小於等於 \(d\) 的權值總和,由於還有容斥的部分,故符號也要記錄。若節點 \(x\) 詢問為 \(d\) ,對於某一級重心 \(C\) ,距離為 \(dis\) ,對應字首和陣列 \(A\) ,對應符號為 \(s\ (s\in\{1,-1\})\) ,那麼 \(x\) 與 \(C\) 的貢獻就是 \(s\cdot A[d-dis]\)
而帶上修改其實也沒什麼區別,只要把字首和換成樹狀陣列,然後每次修改,對於某一級重心 \(C\) ,在樹狀陣列的 \(dis\) 位置做修改即可。
動態點分治就是把點分治的過程用適當容器去維護的演算法。
程式碼
#include<bits/stdc++.h> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) typedef long long LL; using namespace std; const int N=1e5+5; template<const int maxn,const int maxm>struct Linked_list { int head[maxn],to[maxm],nxt[maxm],tot; Linked_list(){clear();} void clear(){memset(head,-1,sizeof(head));tot=0;} void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;} #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i]) }; struct FenwickTree { #define lowbit(x) ((x)&-(x)) vector<int>c;int n; void build(int _n){c.clear();FOR(i,0,n=_n+1)c.push_back(0);} void update(int k,int val){for(k++;k<=n;k+=lowbit(k))c[k]+=val;} int query(int k){int res=0;for(k=min(k+1,n);k>0;k^=lowbit(k))res+=c[k];return res;} #undef lowbit }; Linked_list<N,N<<1>G; FenwickTree FT[N*2];int Fc; int Fid[N][45],dis[N][45],lv[N];bool sgn[N][45]; int sz[N];bool mark[N]; int pw[N],n,q; void CFS(int u,int f,int tot,int &C,int &Mi) { sz[u]=1;int res=0; EOR(i,G,u) { int v=G.to[i]; if(v==f||mark[v])continue; CFS(v,u,tot,C,Mi); sz[u]+=sz[v]; res=max(res,sz[v]); } res=max(res,tot-sz[u]); if(res<Mi)C=u,Mi=res; } void dfs_init(int u,int f,int D,bool s) { Fid[u][++lv[u]]=Fc,dis[u][lv[u]]=D,sgn[u][lv[u]]=s; EOR(i,G,u) { int v=G.to[i]; if(v==f||mark[v])continue; dfs_init(v,u,D+1,s); } } int dfs_dep(int u,int f,int d) { int res=d; EOR(i,G,u) { int v=G.to[i]; if(v==f||mark[v])continue; res=max(res,dfs_dep(v,u,d+1)); } return res; } void dac(int u,int tot) { int Mi=1e9; CFS(u,0,tot,u,Mi); mark[u]=1; FT[++Fc].build(dfs_dep(u,0,0)); dfs_init(u,0,0,1); EOR(i,G,u) { int v=G.to[i]; if(mark[v])continue; FT[++Fc].build(dfs_dep(v,u,1)); dfs_init(v,u,1,0); dac(v,sz[u]>sz[v]?sz[v]:tot-sz[u]); } } void update(int u,int val) { FOR(i,1,lv[u]) { int v=Fid[u][i],w=dis[u][i]; FT[v].update(w,val); } } int query(int u,int d) { int res=0; FOR(i,1,lv[u]) { int v=Fid[u][i],w=dis[u][i];bool s=sgn[u][i]; if(s)res+=FT[v].query(d-w); else res-=FT[v].query(d-w); } return res; } int main() { while(~scanf("%d%d",&n,&q)) { G.clear(); FOR(i,1,n)scanf("%d",&pw[i]); FOR(i,1,n-1) { int u,v; scanf("%d%d",&u,&v); G.add(u,v),G.add(v,u); } Fc=0; memset(lv,0,sizeof(lv)); memset(mark,0,sizeof(mark)); dac(1,n); FOR(i,1,n)update(i,pw[i]); while(q--) { char str[5];int x,y; scanf("%s%d%d",str,&x,&y); if(str[0]=='!') { update(x,y-pw[x]); pw[x]=y; } else if(str[0]=='?')printf("%d\n",query(x,y)); } } return 0; }