【NOIP2016提高】天天愛跑步的解析(樹上差分+LCA+桶)
阿新 • • 發佈:2018-11-10
題目:luogu1600.
題目大意:給定一棵樹和樹上每個節點的,現在給出m對和,表示從到會有一個人沿樹上的路徑走過,並且這個人每秒移動到下一個點.現在每個人都在時刻0走出,詢問每一個點i,請你輸出第時刻有多少人會在點i.
這道題真的是史上最難的NOIP題啊...
我們現在一步步分析部分分,一步步逼近正解.
部分分1:s=1.
若s=1,很容易發現一個人i會對點j做出貢獻,僅當有一個祖先為j且.
我們可以記錄一個計數陣列cnt,也就是一個桶,當我們退出一個點k時,我們將加以k為終點的路徑數量,並計算出答案.
部分分2:t=1.
若t=1,很容易發現一個人i會對點j做出貢獻,僅當
我們將這個式子變式即可得到.
s=1類似的,記錄一個計數陣列cnt,當我們進入一個點k時,我們將加以k為起點的路徑數量,並計算答案.
部分分3:鏈.
如果是鏈的話,那麼我們可以發現一個人要麼是一直往右走,要麼是一直往左走.
那麼我們將人分為兩種,一種是往左走的,一種是往右走的.
然後我們依舊記錄一個桶,往左走的我們從1開始遞迴,往右走的我們從n開始遞迴.
當我們往左走時,我們退出一個點時,我們將這個點的答案計算出來,然後將所有已它為終點的起點的貢獻去掉,這樣就能保證不對後面的答案產生影響,具體這個功能可以用vector實現.
AC做法.
AC做法其實就是鏈的資料做法的拓展.
我們只需要將一條路徑分成兩條鏈,一條從終點到lca,一條從起點到lca.
拆完鏈之後發現這是一個樹上差分,不過要注意若LCA計入了兩次,要去掉一次.
那麼很容易就可以做了.
還有這道題其實是可以寫dsu on tree和線段樹合併的,思路就是因為有子樹查詢cnt陣列,有興趣的自己去寫寫吧,我放棄了.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; const int N=300000; int n,m; int s[N+9],t[N+9],w[N+9]; int lca[N+9],dis[N+9]; int cnt[3*N+9],ans[N+9],num[N+9]; vector<int>q1[N+9],q2[N+9],q3[N+9]; struct side{ int y,next; }e[N*2+9]; int lin[N+9],top; int deep[N+9],gr[N+9][20]; bool use[N+9]; void ins(int x,int y){ e[++top].y=y; e[top].next=lin[x]; lin[x]=top; } void dfs_lca(int k,int fa){ gr[k][0]=fa; deep[k]=deep[fa]+1; for (int i=1;i<19;i++) gr[k][i]=gr[gr[k][i-1]][i-1]; for (int i=lin[k];i;i=e[i].next) if (fa^e[i].y) dfs_lca(e[i].y,k); } int LCA(int x,int y){ if (deep[x]<deep[y]) swap(x,y); int dep=deep[x]-deep[y]; for (int i=0;i<19;i++) if (dep>>i&1) x=gr[x][i]; for (int i=18;i>=0;i--) if (gr[x][i]^gr[y][i]) x=gr[x][i],y=gr[y][i]; return x^y?gr[x][0]:x; } #define vt vector<int>::iterator void dfs_up(int k){ int now=cnt[deep[k]+w[k]]; for (int i=lin[k];i;i=e[i].next) if (e[i].y^gr[k][0]) dfs_up(e[i].y); cnt[deep[k]]+=num[k]; ans[k]+=cnt[deep[k]+w[k]]-now; for (vt it=q1[k].begin();it!=q1[k].end();it++) cnt[deep[*it]]--; } void dfs_down(int k){ int now=cnt[w[k]-deep[k]+N]; for (int i=lin[k];i;i=e[i].next) if (gr[k][0]^e[i].y) dfs_down(e[i].y); for (vt it=q2[k].begin();it!=q2[k].end();it++) cnt[*it+N]++; ans[k]+=cnt[w[k]-deep[k]+N]-now; for (vt it=q3[k].begin();it!=q3[k].end();it++) cnt[*it+N]--; } #undef vt Abigail into(){ scanf("%d%d",&n,&m); int x,y; for (int i=1;i<n;i++){ scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } for (int i=1;i<=n;i++) scanf("%d",&w[i]); for (int i=1;i<=m;i++) scanf("%d%d",&s[i],&t[i]); } Abigail work(){ dfs_lca(1,0); for (int i=1;i<=m;i++){ lca[i]=LCA(s[i],t[i]); dis[i]=deep[s[i]]+deep[t[i]]-2*deep[lca[i]]; num[s[i]]++; q1[lca[i]].push_back(s[i]); q2[t[i]].push_back(dis[i]-deep[t[i]]); q3[lca[i]].push_back(dis[i]-deep[t[i]]); } dfs_up(1); dfs_down(1); for (int i=1;i<=m;i++) if (deep[s[i]]==deep[lca[i]]+w[lca[i]]) ans[lca[i]]--; //去重 } Abigail outo(){ for (int i=1;i<=n;i++) printf("%d ",ans[i]); puts(""); } int main(){ into(); work(); outo(); return 0; }