长链剖分优化树形dp
apio鐵牌告辭(開場想打暴力然后gedit碼代碼5個小時沒寫完三題最低檔暴力真是快樂),聽課也就學到了一丟丟這個東西。
模板題:
https://www.luogu.org/problemnew/show/P5384
首先k級兄弟可以一遍dfs的時候丟到k級父親上變成求k級孩子的詢問。
求k級孩子有個很簡單的做法,直接dfs的時候維護掃到某個點時記錄下的每種dep出現次數,對于所有求這個點k級孩子的詢問減去當前出現次數,dfs完這棵樹后在加上當前出現次數即可。復雜度O(n)。
代碼:
#include<bits/stdc++.h> using namespace std; const int N=1e6+100;template<class T> void rd(T &x) {char c=getchar();x=0;bool f=0;while(!isdigit(c))f|=(c=='-'),c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();if(f)x=-x; }template<class T> void print(T x) {if(x<0)x=-x,putchar('-');if(x==0){putchar('0');return;}static int buf[30],bnum;bnum=0;while(x)buf[++bnum]=x%10,x/=10;for(int i=bnum;i>=1;i--)putchar(buf[i]+'0'); }struct pii{int fi,sc;pii(){};pii(int fi,int sc):fi(fi),sc(sc){}; };int n,Q,fa[N],ans[N],stk[N],snum=0,cnt[N],dep[N]; vector<int>son[N]; vector<pii>q[N];void dfs(int x) {stk[++snum]=x;for(int i=0,v;i<q[x].size();i++){v=snum-q[x][i].fi;if(v<=0||q[x][i].fi==0)ans[q[x][i].sc]=1;else q[stk[v]].push_back(pii(q[x][i].fi,q[x][i].sc));}q[x].clear();for(int i=0;i<son[x].size();i++)dfs(son[x][i]);--snum; }void dfs1(int x) {for(int i=0;i<q[x].size();i++)ans[q[x][i].sc]-=cnt[dep[x]+q[x][i].fi];cnt[dep[x]]++;for(int i=0;i<son[x].size();i++)dep[son[x][i]]=dep[x]+1,dfs1(son[x][i]);for(int i=0;i<q[x].size();i++)ans[q[x][i].sc]+=cnt[dep[x]+q[x][i].fi]; }int main() {rd(n),rd(Q);for(int i=2;i<=n;i++){rd(fa[i]),son[fa[i]].push_back(i);}for(int i=1,u,k;i<=Q;i++){rd(u),rd(k);q[u].push_back(pii(k,i));}dfs(1),dep[1]=1,dfs1(1);for(int i=1;i<=Q;i++)print(ans[i]-1),putchar(' '); }好吧這種做法不是重點。。。
考慮naive的做法,記錄f(i,j)表示i子樹下距離i為j的點有多少個,然后逐層合并。優化:首先一個點記的子樹下深度的點一定是大于當前深度的,然后考慮每個點初始時可以繼承一個孩子的信息,而如果這個點取子樹深度最深的點,就能證明總復雜度也是O(n)的了。因為這樣的繼承相當于在繼承該點子樹內長鏈剖分的長鏈信息,而發現對于每一條剖分的鏈,它作為短鏈只會在鏈頂端被用于拷貝一次,而拷貝的復雜度就是鏈長,所以總復雜度是O(n)的,空間基本證明也一樣。
(實現時繼承那步要用指針以實現偏移1的操作)
代碼:(被毒瘤卡空間被迫換方式存邊)
#include<bits/stdc++.h> using namespace std; const int N=1e6+100;template<class T> void rd(T &x) {char c=getchar();x=0;bool f=0;while(!isdigit(c))f|=(c=='-'),c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();if(f)x=-x; }template<class T> void print(T x) {if(x<0)x=-x,putchar('-');if(x==0){putchar('0');return;}static int buf[30],bnum;bnum=0;while(x)buf[++bnum]=x%10,x/=10;for(int i=bnum;i>=1;i--)putchar(buf[i]+'0'); }struct pii{int fi,sc;pii(){};pii(int fi,int sc):fi(fi),sc(sc){}; };int n,Q,ans[N],stk[N],snum=0,mxd[N],lson[N],*f[N],*mem; vector<pii>q[N];int hd[N],nxt[N],to[N],tot=0; void add(int x,int y) {nxt[++tot]=hd[x],to[tot]=y,hd[x]=tot;}void dfs(int x) {stk[++snum]=x,lson[x]=0,mxd[x]=1;for(int i=0,v;i<q[x].size();i++){v=snum-q[x][i].fi;if(v<=0||q[x][i].fi==0)ans[q[x][i].sc]=1;else q[stk[v]].push_back(pii(q[x][i].fi,q[x][i].sc));}q[x].clear();for(int i=hd[x],v;i;i=nxt[i]){v=to[i],dfs(v);if(mxd[lson[x]]<mxd[v])lson[x]=v;}mxd[x]=mxd[lson[x]]+1;--snum; }void dfs1(int x,int *fa_f) {f[x]=fa_f,f[x][0]=1;if(lson[x])dfs1(lson[x],f[x]+1);for(int i=hd[x],v;i;i=nxt[i]){v=to[i];if(v==lson[x])continue;int *tmp=mem;mem+=mxd[v];dfs1(v,tmp);for(int j=0;j<mxd[v];j++)f[x][j+1]+=tmp[j];}for(int i=0;i<q[x].size();i++)ans[q[x][i].sc]=f[x][q[x][i].fi]; }int main() {rd(n),rd(Q);for(int i=2,fa;i<=n;i++)rd(fa),add(fa,i);for(int i=1,u,k;i<=Q;i++){rd(u),rd(k);q[u].push_back(pii(k,i));}dfs(1);mem=(int *)malloc(sizeof(int)*N);dfs1(1,(int *)malloc(sizeof(int)*N));for(int i=1;i<=Q;i++)print(ans[i]-1),putchar(' '); }這種依靠長鏈剖分優化樹形dp的做法還可以改一改在一些自頂向上傳遞信息的樹上問題使用,具體做法就是對于當前點子樹內長鏈的那個長兒子,直接把字樹內其它短鏈信息丟給它,根據之前證明是O(n)的。對于短鏈,則用當前子樹信息減去這條短鏈里的信息,發現與之前幾乎一樣也是O(n),總復雜度就是O(n)的了。(具體題目還沒寫過,瞎bb)
總結
以上是生活随笔為你收集整理的长链剖分优化树形dp的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吴恩达深度学习思维导图--来自刚入门的学
- 下一篇: 图书管理有没有必要使用进出库管理系统?