Codeforces 671D. Roads in Yusland 題解
阿新 • • 發佈:2020-08-28
題目連結:D. Roads in Yusland
題目大意:洛谷
題解:這題目前會的有兩種做法。
第一種比較簡單,設\(f[i][j]\)為到了第\(i\)個點,鏈上覆蓋到深度為\(j\)是的最小代價,然後寫出轉移方程,是\(f[i][j]=\sum {f[son][k]}(\max k=j)\),然後發現可以線段樹合併,所以直接寫就可以了,注意一下需要回收空間。
第二種是設\(f[i]\)表示覆蓋了\(i\)的子樹以及它的父邊的最小代價,這個 DP 有一些麻煩,因為我們的資訊有一些少,考慮如何轉移,對於\(x\in son(y)\),將\(x\)中的資訊轉移到\(y\)上,對於一個權值為\(k\)
- 如果向上延伸超過了\(y\),那麼\(y\)的權值應為\(k+\sum_{z\in son(y)}f[z]- f[x]\)。
- 如果恰好延伸到\(y\),那麼不合法,應當捨棄。
所以說,我們只需要維護一個小根堆即可。
時間複雜度均為\(O(n\log n)\)。
#include <vector> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int Maxn=300000; int n,m; int root[Maxn+5]; int d[Maxn+5],tot; ll f[Maxn+5],ans; vector<int> edge[Maxn+5]; vector<pair<int,int> > p[Maxn+5]; struct Node{ pair<ll,int> x; ll z; int l,r,f,d; }t[Maxn+5]; void add(int x,ll k){ if(x){ t[x].x.first+=k; t[x].z+=k; } } void spd(int x){ if(t[x].z){ add(t[x].l,t[x].z); add(t[x].r,t[x].z); t[x].z=0; } } int merge(int x,int y){ if(x==0||y==0){ return x|y; } if(t[x].x>t[y].x){ swap(x,y); } spd(x); t[t[x].r=merge(t[x].r,y)].f=x; if(t[t[x].r].d>t[t[x].l].d){ swap(t[x].l,t[x].r); } t[x].d=t[t[x].r].d+1; return x; } void dfs(int x,int fa){ d[x]=d[fa]+1; for(pair<int,int> v:p[x]){ t[++tot].x=v; t[tot].d=1; root[x]=merge(root[x],tot); } ll s=0; for(int y:edge[x]){ if(y!=fa){ dfs(y,x); s+=f[y]; add(root[y],-f[y]); root[x]=merge(root[x],root[y]); } } add(root[x],s); while(root[x]&&d[t[root[x]].x.second]>=d[x]){ spd(root[x]); root[x]=merge(t[root[x]].l,t[root[x]].r); } if(root[x]==0){ puts("-1"); exit(0); } f[x]=t[root[x]].x.first; } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<n;i++){ scanf("%d%d",&x,&y); edge[x].push_back(y); edge[y].push_back(x); } for(int i=1,x,y,z;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); p[x].push_back(make_pair(z,y)); } d[1]=1; for(int x:edge[1]){ dfs(x,1); ans+=f[x]; } printf("%lld\n",ans); return 0; }