1. 程式人生 > >[codeforces contest 1119 F] Niyaz and Small Degrees 解題報告 (樹形DP)

[codeforces contest 1119 F] Niyaz and Small Degrees 解題報告 (樹形DP)

small inline 需要 clear tchar printf inter sizeof else

interlinkage:

http://codeforces.com/contest/1119/problem/F

description:

技術分享圖片

有一顆$n$個節點的樹,每條邊有一個邊權

對於一個$x$,刪去一些邊使得每個點的度數都不超過$x$,最小化刪去邊的邊權並輸出

需要一次輸出$x=0->n-1$的值

$1<=n<=250000$

solution:

  • part1
  • 先考慮單個$x$的做法。任選一個根,設$f_{u,0/1}$表示以節點$u$為根的子樹內,節點$u$與它的父親不斷/斷的最小代價;
  • 顯然這個可以討論轉移,但我們換一個更好的角度;
  • 設$v$為$u$的一個兒子,若$f_{v,1}+c<=f_{v,0}$,即斷的話比不斷更優秀的,那麽我們一定選擇斷,因為這樣還可以讓$u$的度數小$1$。這種情況我們就直接加上$f_{v,1}+c$,並讓$u$度數$--$;
  • 否則的話我們就先假設這條邊不斷,加上$f_{v,0}$。那麽到最後可能會發現$u$的度數不滿足不超過$x$,顯然我們要把若幹個$f_{v,0}$變成$f_{v,1}+c$;
  • 我們對每個點維護一個$f_{v,1}+c-f_{v,0}$的堆,取度數個最小的就好了;
  • 這裏的堆本質上維護的就是通過多大的代價能讓度數減一;
  • part2
  • 現在考慮多個$x$,從小做到大
  • 有一個很顯然的想法,若是一個點原來的度數就不超過$x$,那麽這個點可以直接刪掉;
  • 做法是把所有以這個點為端點的邊放到這些邊的另一個端點的堆裏面,表示可以通過斷掉這條邊來使得度數減一;
  • 剩下的就是對所有有用的點,即度數大於$x$的點做上面的$DP$就好了;
  • part3
  • 復雜度顯然為$\sum_{x=0}^{n-1}\sum_{i=1}^{n}[du_i>x]=\sum_{i=1}^{n}du_i=O(n)$;

code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define fi first
#define se second
#define pb push_back
using
namespace std; typedef long long ll; typedef pair<ll,ll> pll; const ll N=250005; ll n; ll du[N],nxt[N],vis[N]; ll sum[N]; vector <pll> e[N]; vector <ll> d[N]; inline ll read() { char ch=getchar();ll s=0,f=1; while (ch<0||ch>9) {if (ch==-) f=-1;ch=getchar();} while (ch>=0&&ch<=9) {s=(s<<3)+(s<<1)+ch-0;ch=getchar();} return s*f; } bool cmp(pll x,pll y) {return du[x.fi]<du[y.fi];} struct node { priority_queue <ll> A,B; void push(ll x) {A.push(x);} void del(ll x) {B.push(x);} ll top() {while (!B.empty()&&A.top()==B.top()) A.pop(),B.pop();return A.top();} void pop() {top();A.pop();} ll size() {return A.size()-B.size();} }h[N]; void upd(ll x,ll num) { while (h[x].size()>num) { sum[x]=sum[x]-h[x].top(); h[x].pop(); } } void upd1(ll x,ll num,vector <ll> &add) { while (h[x].size()>num) { sum[x]=sum[x]-h[x].top(); add.pb(h[x].top()); h[x].pop(); } } void dele(ll x) { vis[x]=1; for (ll i=0;i<e[x].size();i++) { ll y=e[x][i].fi,c=e[x][i].se;; if (vis[y]) continue; h[y].push(c);sum[y]=sum[y]+c; } } ll D; ll f[N][2],st[N]; void dfs(ll x) { vis[x]=1;ll num=du[x]-D; upd(x,num); vector <ll> add,del; add.clear();del.clear(); ll siz=e[x].size(),tot=0; while (st[x]<siz&&du[e[x][st[x]].fi]<=D) st[x]++; for (ll i=st[x];i<siz;i++) { ll y=e[x][i].fi,c=e[x][i].se;; if (vis[y]) continue; dfs(y); if (f[y][1]+c<=f[y][0]) {num--;tot=tot+f[y][1]+c;} else { tot=tot+f[y][0]; ll o=f[y][1]+c-f[y][0]; del.pb(o); h[x].push(o); sum[x]=sum[x]+o; } } upd1(x,max(0ll,num),add); f[x][0]=tot+sum[x]; upd1(x,max(0ll,num-1),add); f[x][1]=tot+sum[x]; for (ll i=0;i<add.size();i++) h[x].push(add[i]),sum[x]+=add[i]; for (ll i=0;i<del.size();i++) h[x].del(del[i]),sum[x]-=del[i]; } int main() { n=read(); ll ans=0; for (ll i=1;i<n;i++) { ll x=read(),y=read(),c=read(); e[x].pb({y,c});e[y].pb({x,c}); du[x]++;du[y]++; ans+=c; } printf("%I64d ",ans); for (ll i=1;i<=n;i++) { d[du[i]].pb(i); sort(e[i].begin(),e[i].end(),cmp); } nxt[n]=n+1; for (ll i=n-1;i>=1;i--) { if (d[i+1].size()) nxt[i]=i+1; else nxt[i]=nxt[i+1]; } memset(vis,0,sizeof(vis)); for (ll u=1;u<n;u++) { for (ll i=0;i<d[u].size();i++) dele(d[u][i]); ans=0;D=u; for (ll i=u+1;i<n;i=nxt[i]) for (ll j=0;j<d[i].size();j++) { if (vis[d[i][j]]) continue; dfs(d[i][j]); ans=ans+f[d[i][j]][0]; } for (ll i=u+1;i<n;i=nxt[i]) for (ll j=0;j<d[i].size();j++) vis[d[i][j]]=0; printf("%I64d ",ans); } return 0; }

[codeforces contest 1119 F] Niyaz and Small Degrees 解題報告 (樹形DP)