http://codeforces.com/contest/802/problem/K
【题意】
给定一棵树,Heidi从根结点0出发沿着边走,每个结点最多经过k次,求这棵树的最大花费是多少(同一条边走n次花费只算一次)
【思路】
对于结点v:
- 如果在v的某棵子树停下,那么可以“遍历”k棵子树(有的话)
- 如果还要沿着v返回v的父节点p,那么只能“遍历”k-1棵子树(有的话)。
用dp[v][1]表示第一种情况,dp[v][0]表示第二种情况;最后要求的就是dp[0][0]。
1. 对于dp[v][1],把所有的子树从大到小排序
(t=k-1)
2. 对于dp[v][0],枚举子结点dp[u][0]中的u,剩下的k-1个dp[u][1]取最大的,所以我们可以这样预处理:
sum=
(t=k)
- 如果u<k,则target=sum-dp[u][1]+dp[u][0]
- 否则, target=sum-dp[t][1]+dp[u][0](t是从大到小排序后的第k-1个)
这样,dp[0][0]就是所求结果(dp[0][0]一定大于dp[0][1]),时间复杂度是O(nlogn)
【官方题解】
【Accepted】
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<vector> 7 #include<algorithm> 8 9 using namespace std; 10 int n,m; 11 vector< vector< pair<int,int> > > g; 12 const int maxn=1e5+5; 13 int dp[maxn][2]; 14 void dfs(int v,int p,int edge) 15 { 16 //从p到v的花费要算在v里 17 dp[v][0]+=edge; 18 dp[v][1]+=edge; 19 vector< pair<int,int> > s; 20 //只有根结点没有父节点,非根结点有父节点,减去1 21 if(v==0) 22 { 23 s.resize(g[v].size()); 24 } 25 else 26 { 27 s.resize(g[v].size()-1); 28 } 29 //遍历 30 int num=0; 31 for(int i=0;i<g[v].size();i++) 32 { 33 int to=g[v][i].first; 34 if(to==p) 35 { 36 continue; 37 } 38 dfs(to,v,g[v][i].second); 39 s[num++]={dp[to][1],to}; 40 } 41 //从大到小排序 42 sort(s.begin(),s.end()); 43 reverse(s.begin(),s.end()); 44 //要记录各个子结点的rank,后面dp[v][0]枚举u是要分类 45 int pos[maxn]; 46 for(int i=0;i<s.size();i++) 47 { 48 pos[s[i].second]=i; 49 } 50 //计算dp[v][1] 51 for(int i=0;i<min(m-1,(int)s.size());i++) 52 { 53 dp[v][1]+=s[i].first; 54 } 55 //计算dp[v][0] 56 int sum=0; 57 for(int i=0;i<min(m,(int)s.size());i++) 58 { 59 sum+=s[i].first; 60 } 61 int maxu=-1; 62 //枚举 63 for(int i=0;i<g[v].size();i++) 64 { 65 int to=g[v][i].first; 66 if(to==p) 67 { 68 continue; 69 } 70 if(pos[to]<m) 71 { 72 maxu=max(maxu,sum-dp[to][1]+dp[to][0]); 73 } 74 else 75 { 76 maxu=max(maxu,sum-s[m-1].first+dp[to][0]); 77 } 78 } 79 if(maxu>-1) 80 { 81 dp[v][0]+=maxu; 82 } 83 } 84 int main() 85 { 86 while(~scanf("%d%d",&n,&m)) 87 { 88 memset(dp,0,sizeof(dp)); 89 g.resize(n); 90 int u,v,c; 91 for(int i=0;i<n-1;i++) 92 { 93 scanf("%d%d%d",&u,&v,&c); 94 g[u].push_back({v,c}); 95 g[v].push_back({u,c}); 96 } 97 //根结点为0,无父结点,根结点到父结点的花费也为0 98 dfs(0,0,0); 99 printf("%d\n",dp[0][0]); 100 } 101 return 0; 102 }