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】

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<algorithm> using namespace std;
int n,m;
vector< vector< pair<int,int> > > g;
const int maxn=1e5+;
int dp[maxn][];
void dfs(int v,int p,int edge)
{
//从p到v的花费要算在v里
dp[v][]+=edge;
dp[v][]+=edge;
vector< pair<int,int> > s;
//只有根结点没有父节点,非根结点有父节点,减去1
if(v==)
{
s.resize(g[v].size());
}
else
{
s.resize(g[v].size()-);
}
//遍历
int num=;
for(int i=;i<g[v].size();i++)
{
int to=g[v][i].first;
if(to==p)
{
continue;
}
dfs(to,v,g[v][i].second);
s[num++]={dp[to][],to};
}
//从大到小排序
sort(s.begin(),s.end());
reverse(s.begin(),s.end());
//要记录各个子结点的rank,后面dp[v][0]枚举u是要分类
int pos[maxn];
for(int i=;i<s.size();i++)
{
pos[s[i].second]=i;
}
//计算dp[v][1]
for(int i=;i<min(m-,(int)s.size());i++)
{
dp[v][]+=s[i].first;
}
//计算dp[v][0]
int sum=;
for(int i=;i<min(m,(int)s.size());i++)
{
sum+=s[i].first;
}
int maxu=-;
//枚举
for(int i=;i<g[v].size();i++)
{
int to=g[v][i].first;
if(to==p)
{
continue;
}
if(pos[to]<m)
{
maxu=max(maxu,sum-dp[to][]+dp[to][]);
}
else
{
maxu=max(maxu,sum-s[m-].first+dp[to][]);
}
}
if(maxu>-)
{
dp[v][]+=maxu;
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(dp,,sizeof(dp));
g.resize(n);
int u,v,c;
for(int i=;i<n-;i++)
{
scanf("%d%d%d",&u,&v,&c);
g[u].push_back({v,c});
g[v].push_back({u,c});
}
//根结点为0,无父结点,根结点到父结点的花费也为0
dfs(,,);
printf("%d\n",dp[][]);
}
return ;
}

注意vector开始要resize.....orz

【WA】

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath> using namespace std;
int n,k;
const int maxn=2e5+;
struct edge
{
int to;
int nxt;
int c;
}e[maxn];
int head[maxn];
int tot;
struct node
{
int x;
int id;
}sz[maxn];
int rk[maxn];
bool cmp(node a,node b)
{
return a.x>b.x;
}
void init()
{
memset(head,-,sizeof(head));
tot=;
} void add(int u,int v,int c)
{
e[tot].to=v;
e[tot].c=c;
e[tot].nxt=head[u];
head[u]=tot++;
}
int dp[maxn][]; int dfs(int u,int pa,int c)
{
dp[u][]=c;
dp[u][]=c;
int cnt=;
for(int i=head[u];i!=-;i=e[i].nxt)
{
int v=e[i].to;
int c=e[i].c;
if(v==pa) continue;
dfs(v,u,c);
sz[cnt].x=dp[v][];
sz[cnt++].id=v;
}
sort(sz,sz+cnt,cmp);
for(int i=;i<min(cnt,k-);i++)
{
dp[u][]+=sz[i].x;
}
int sum=;
for(int i=;i<min(cnt,k);i++)
{
sum+=sz[i].x;
}
int ans=;
for(int i=;i<cnt;i++)
{
if(i<k)
{
ans=max(ans,sum-sz[i].x+dp[sz[i].id][]);
}
else
{
ans=max(ans,sum-sz[k-].x+dp[sz[i].id][]);
}
}
dp[u][]+=ans;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
init();
memset(dp,,sizeof(dp));
for(int i=;i<n-;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,c);
}
dfs(,-,);
cout<<dp[][]<<endl;
}
return ;
}

Wrong Answer

终于弄清楚了这个为什么WA!因为我在dfs里用了一个全局变量sz来保存{dp[v][1],v}。然而这是一个全局变量,所以一层里的正确值会被另一层修改!比如当我递归到0时已经有了正确值sz[0].w=5,sz[0].v=2;然而再递归到0的另一分枝1的时候,会修改sz[0],最后再回溯到0时sz[0]已经不是当年的sz[0]了!

所以还是用vector临时申请吧!

【AC(一个更优美的代码)】

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath> using namespace std;
int n,k;
const int maxn=2e5+;
struct edge
{
int to;
int nxt;
int c;
}e[maxn];
int head[maxn];
int tot;
int dp[maxn][]; struct node
{
int x;
int id;
node(){}
node(int _x,int _id):x(_x),id(_id){}
bool operator<(const node & nd) const
{
return x>nd.x;
}
}; void init()
{
memset(head,-,sizeof(head));
tot=;
} void add(int u,int v,int c)
{
e[tot].to=v;
e[tot].c=c;
e[tot].nxt=head[u];
head[u]=tot++;
} int dfs(int u,int pa,int c)
{
dp[u][]=c;
dp[u][]=c;
vector<node> s;
for(int i=head[u];i!=-;i=e[i].nxt)
{
int v=e[i].to;
int c=e[i].c;
if(v==pa) continue;
dfs(v,u,c);
s.push_back(node(dp[v][],v));
}
sort(s.begin(),s.end());
int sz=s.size();
for(int i=;i<min(sz,k-);i++)
{
dp[u][]+=s[i].x;
}
int sum=;
for(int i=;i<min(sz,k);i++)
{
sum+=s[i].x;
}
int ans=;
for(int i=;i<sz;i++)
{
if(i<k)
{
ans=max(ans,sum-s[i].x+dp[s[i].id][]);
}
else
{
ans=max(ans,sum-s[k-].x+dp[s[i].id][]);
}
}
dp[u][]+=ans;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
init();
memset(dp,,sizeof(dp));
for(int i=;i<n-;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,c);
}
dfs(,-,);
cout<<dp[][]<<endl;
}
return ;
}

如果是vector<pair<int,int>> 要从大到小排序,可以先sort(s.begin(),s.end()),再reverse(s.begin(),s.end())

【树形DP】codeforces K. Send the Fool Further! (medium)的更多相关文章

  1. 树形DP ---- Codeforces Global Round 2 F. Niyaz and Small Degrees引发的一场血案

    Aspirations:没有结果,没有成绩,acm是否有意义?它最大的意义就是让我培养快速理解和应用一个个未知知识点的能力. ————————————————————————————————————— ...

  2. 树形dp - Codeforces Round #322 (Div. 2) F Zublicanes and Mumocrates

    Zublicanes and Mumocrates Problem's Link Mean: 给定一个无向图,需要把这个图分成两部分,使得两部分中边数为1的结点数量相等,最少需要去掉多少条边. ana ...

  3. 树形DP Codeforces Round #135 (Div. 2) D. Choosing Capital for Treeland

    题目传送门 /* 题意:求一个点为根节点,使得到其他所有点的距离最短,是有向边,反向的距离+1 树形DP:首先假设1为根节点,自下而上计算dp[1](根节点到其他点的距离),然后再从1开始,自上而下计 ...

  4. 树形dp Codeforces Round #364 (Div. 1)B

    http://codeforces.com/problemset/problem/700/B 题目大意:给你一棵树,给你k个树上的点对.找到k/2个点对,使它在树上的距离最远.问,最大距离是多少? 思 ...

  5. Codeforces 802L Send the Fool Further! (hard)

    Description 题面 题目大意:求从根节点出发,每次随机走一个相邻的点,问走到任意一个叶子节点经过的路径长度的期望(走到就停止) Solution 树上高斯消元,复杂度是 \(O(n)\) 的 ...

  6. VK Cup 2012 Round 1 D. Distance in Tree (树形dp)

    题目:http://codeforces.com/problemset/problem/161/D 题意:给你一棵树,问你两点之间的距离正好等于k的有多少个 思路:这个题目的内存限制首先大一倍,他有5 ...

  7. codeforces 161D Distance in Tree 树形dp

    题目链接: http://codeforces.com/contest/161/problem/D D. Distance in Tree time limit per test 3 secondsm ...

  8. Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)

    题目:http://codeforces.com/contest/1153/problem/D 题意:给你一棵树,每个节点有一个操作,0代表取子节点中最小的那个值,1代表取子节点中最大的值,叶子节点的 ...

  9. Codeforces 1097G Vladislav and a Great Legend [树形DP,斯特林数]

    洛谷 Codeforces 这题真是妙的很. 通过看题解,终于知道了\(\sum_n f(n)^k​\)这种东西怎么算. update:经过思考,我对这题有了更深的理解,现将更新内容放在原题解下方. ...

随机推荐

  1. 动手实现 Redux(六):Redux 总结

    不知不觉地,到这里大家不仅仅已经掌握了 Redux,而且还自己动手写了一个 Redux.我们从一个非常原始的代码开始,不停地在发现问题.解决问题.优化代码的过程中进行推演,最后把 Redux 模式自己 ...

  2. Windows API函数大全四

    10. API之硬件与系统函数 ActivateKeyboardLayout 激活一个新的键盘布局.键盘布局定义了按键在一种物理性键盘上的位置与含义 Beep 用于生成简单的声音 CharToOem ...

  3. Android学习笔记(十九) OkHttp

    一.概述 根据我的理解,OkHttp是为了方便访问网络或者获取服务器的资源,而封装出来的一个工具包.通常的使用步骤是:首先初始化一个OkHttpClient对象,然后使用builder模式构造一个Re ...

  4. http://blog.chinaunix.net/uid-9845710-id-1996675.html snmpd配置

    http://blog.chinaunix.net/uid-9845710-id-1996675.html http://lihuipeng.blog.51cto.com/3064864/643960 ...

  5. 利用Jenkins打包ISO和QCOW2镜像文件

    现在的云虚拟化环境越来越多,经常会碰到需要修改并重新打包新的ISO或QCOW2镜像文件.通过手工的方式会比较麻烦,所以在镜像发布的生产环境中可以利用Jenkins来进行定期打包发布,以下介绍Jenki ...

  6. HDU 5768Lucky7(多校第四场)容斥+中国剩余定理(扩展欧几里德求逆元的)+快速乘法

    地址:http://acm.hdu.edu.cn/showproblem.php?pid=5768 Lucky7 Time Limit: 2000/1000 MS (Java/Others)    M ...

  7. springcloud 之 bus 消息总线

    在分布式系统中,我们通常使用轻量级消息代理(rabbitmq.kafuka)建立一个公共的主题,让所有的微服务都链接进来,并且监听消费这个主题的内容,我们就称这个主题是 消息总线. (可以用作配置文件 ...

  8. aapt环境变量配置

    D:\android-sdk_r24.4.1-windows\android-sdk-windows\build-tools\28.0.2 将aapt路径添加到path中, 打开cmd 输入aapt

  9. CAD交互绘制带颜色宽度的直线(网页版)

    用户可以在CAD控件视区任意位置绘制直线. 主要用到函数说明: _DMxDrawX::DrawLine 绘制一个直线.详细说明如下: 参数 说明 DOUBLE dX1 直线的开始点x坐标 DOUBLE ...

  10. c++ vector容器遍历方式

    #include <vector> #include <iostream> class Test { public: int a; int b; int c; Test() { ...