1. 程式人生 > 實用技巧 >Codeforces Round #646 (Div. 2) E. Tree Shuffling dfs

Codeforces Round #646 (Div. 2) E. Tree Shuffling dfs

題意:

給你n個節點,這n個節點構成了一顆以1為樹根的樹。每一個節點有一個初始值bi,從任意節點 i 的子樹中選擇任意k個節點,並按他的意願隨機排列這些節點中的數字,從而產生k⋅ai 的成本。對於一個節點i你需要將bi改成ci。

這個bi值和ci值的範圍是[0,1]

題解:

對於一個節點,如果它的bi==ci,那麼我們就不用管它(因為你改變它的值,那麼肯定之後還要花費成本再改回來,增加了成本)。那麼我們首先找出來一共有多少個節點bi!=ci ,然後總權值肯定是

num1*a1+num2*a2...+numn*an

numi表示從節點 i 的子樹中選擇任意numi個節點

那麼肯定是儘可能在ai的值越小的子樹上儘量增加numi的數量,這樣總權值肯定最小

cnt0[i]:以i節點為樹根的子樹,在bi!=ci,bi等於0的數量

cnt1[i]:以i節點為樹根的子樹,在bi!=ci,bi等於1的數量

那麼我們就從樹根開始dfs,並且維護一個ai最小值,在dfs過程中記錄一下i這個節點的所有子節點上bi!=ci的數量(如果bi!=ci,還要記錄一下i這個點的bi值),如果ai等於我們維護的那個ai最小值,那就對這個節點

的2*min(cnt0[i],cnt1[i])個節點進行排序。然後讓cnt1[i]和cnt0[i]都減去min(cnt0[i],cnt1[i])

最後判斷一下cnt1[1]和cnt0[1]是否為0,等於0就代表所有節點bi都等於ci,否則輸出-1

程式碼:

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string>
#include<queue>
#include<deque>
#include<string.h>
#include<map>
#include <iostream>
#include <math.h>
#define Mem(a,b) memset(a,b,sizeof(a))
const double II = acos(-1
); const double PP = (II*1.0)/(180.00); using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int maxn=2e5+10; ll a[maxn],b[maxn],c[maxn],cnt0[maxn],cnt1[maxn],sum; vector<ll>w[maxn]; void dfs(ll x,ll fa,ll minn) { minn=min(minn,a[x]); for(ll i=0;i<w[x].size();++i) { ll now=w[x][i]; if(now!=fa) { dfs(now,x,minn); cnt0[x]+=cnt0[now]; cnt1[x]+=cnt1[now]; } } if(b[x]!=c[x]) { if(b[x]) cnt1[x]++; else cnt0[x]++; } if(minn==a[x]) { ll ans=min(cnt0[x],cnt1[x]); ans*=2; //printf("%d %d****\n",ans,a[x]); sum=sum+ans*a[x]; ans/=2; cnt0[x]-=ans; cnt1[x]-=ans; } } int main() { ll n; sum=0; scanf("%I64d",&n); for(ll i=1;i<=n;++i) { scanf("%I64d%I64d%I64d",&a[i],&b[i],&c[i]); } for(ll i=1;i<n;++i) { ll u,v; scanf("%I64d%I64d",&u,&v); w[u].push_back(v); w[v].push_back(u); } dfs(1,0,INF); if(cnt1[1] || cnt0[1]) { printf("-1\n"); } else printf("%I64d\n",sum); }