P3066 [USACO12DEC]逃跑的BarnRunning Away From
阿新 • • 發佈:2018-11-27
目錄
題目
思路
雖說這個題目有多種做法,但
左偏樹演算法:
我們發現這個合併的時候並不好合並,因為存的值不是固定的
那我們是不是可以lazy陣列呢
因為是兩個顆樹合併,顯然是步闊以的
那就轉換一下思路,什麼是固定的呢
那就是1到i的路徑
我們可以dfs出val[i]表示1到i的路徑和
這個val也就是左偏樹的初始值
然後我們發現,一個樹i的所有後代x
都要經過1到i的所經過的路徑
所以直接維護val,只需要在比較的時候減去val[i]即可
感覺我說的太噁心了,還是去看程式碼吧
錯誤&&注意
這個空間和long long讓我wrong了4次,小心點吧
程式碼
// luogu-judger-enable-o2 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #define FOR(i,a,b) for(int i=a;i<=b;++i) using namespace std; const int maxn=200007; typedef long long ll; inline ll read() { ll x=0,f=1;char s=getchar(); for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1; for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0'; return x*f; } struct edge { int v,nxt; ll q; }e[maxn<<1]; int head[maxn<<1],tot; void add_edge(int u,int v,ll q) { e[++tot].v=v; e[tot].q=q; e[tot].nxt=head[u]; head[u]=tot; } int n; ll m,val[maxn]; int ans[maxn],size[maxn]; int ch[maxn][2],dis[maxn]; int merge(int x,int y) { if(!x||!y) return x+y; if(val[x]<val[y]) swap(x,y); ch[x][1]=merge(ch[x][1],y); if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]); dis[x]=dis[ch[x][1]]+1; return x; } int work1(int x,int y) { int tmp=merge(x,y); y=x^y^tmp; x=tmp; size[x]+=size[y]; return tmp; } int work2(int x) { int tmp=merge(ch[x][0],ch[x][1]); size[tmp]=size[x]-1; return tmp; } int dfs(int u,int f) { ans[u]=1; int rt=u; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(v==f) continue; int tmp=dfs(v,u); rt=work1(rt,tmp); } while(val[rt]-val[u] > m) rt=work2(rt); ans[u]=size[rt]; return rt; } void nb_dfs(int u,int f) { size[u]=1; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].v; if(v==f) continue; val[v]=val[u]+e[i].q; nb_dfs(v,u); } } int main() { n=read(),m=read(); FOR(i,2,n) { int x=read(); ll y=read(); add_edge(i,x,y); add_edge(x,i,y); } nb_dfs(1,0); dfs(1,0); FOR(i,1,n) cout<<ans[i]<<"\n"; return 0; }