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. Suricata的初始化脚本

    见官网 https://suricata.readthedocs.io/en/latest/initscripts.html

  2. Java GC基础

    Java的垃圾回收机制负责回收无用对象占据的内存资源,但是有特殊情况:假定对象不是使用new关键字获得了一块儿“特殊”的内存区域,

  3. P3374 【模板】树状数组 1 单点修改与区间查询

    题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. ...

  4. 搭建SSM框架(聚合项目)

    parents 父工程 pom  base用户权限 jar   wms业务 jar app帮助管理 war1. parents的pom.xml文件 1.1 maven servlet3.1.0 1.2 ...

  5. XDroidMvp 轻量级的Android MVP快速开发框架

    XDroidMvp是XDroidAndroid快速开发框架的MVP版本,其使用方式类似于XDroid,大部分源码也来自XDroid. XDroidMvp主要会有这些特性: 无需写Contract! 无 ...

  6. ubuntu下安装redis扩展

    采用源码编译安装 下载 下载redis源码包到本地一个临时目录 git clone https://github.com/phpredis/phpredis.git 移动文件到合适目录 mv phpr ...

  7. sql 删除重复数据

    DELETE a FROM tbBuilding a WHERE EXISTS (SELECT 1 FROM tbBuilding b WHERE b.Province = a.Province AN ...

  8. 在云环境上使用SLF4J对Java程序进行日志记录

    我开发了一个Java应用,部署到云环境上之后,用postman测试发现不能按照我期望的工作,但是返回的消息对我没有任何帮助. 因为部署在云端的应用很难像本地Java应用一样调试,所以我打算用SLF4J ...

  9. javaweb系列-关于HttpSessionListener的sessionDestroyed什么时候触发

    根据书本写了下面这个监听器,然后开始调试,打开一个浏览器来访问该网页,可以正常触发sessionCreated,然后关闭浏览器,发现没有触发sessionDestroyed,然后我怀疑是不是这个监听器 ...

  10. vc枚举本机端口信息API

    常用的获取端口信息的函数: GetTcpTableGetExtendedTcpTableGetUdpTableGetExtendedUdpTable GetTcp6Table function Get ...