<更新提示>

<第一次更新>


<正文>

快乐链覆盖

Description

给定一棵 n 个点的树,你需要找至多 k 条互不相交的路径,使得它们的长度之和最大

定义两条路径是相交的:当且仅当存在至少一个点,使得这个点在两条路径中都出现

定义一条路径的长度为该路径经过的点的数量

这个题非常简单,非常传统,但为了让它变成一道能一个顶俩的题,出题人决定让你输出任意一组方案。

Input Format

第一行一个正整数 T 表示数据组数

接下来,对于每组数据:

第一行两个整数 n,kn,k

接下来 n−1 行,每行两个整数 a,b 描述一条树边

Output Format

对于每组数据,第一行输出一个整数表示最大的长度之和

之后你要输出任意一组长度之和最大的方案之后你要输出任意一组长度之和最大的方案,第一行一个整数 P 表示你的方案由几条路径构成,接下来 P 行每行两个整数描述一条路径

当然,如果你只能求出答案,不能求出任意一组方案的话,本题也会给你一些部分分,具体看数据范围中的描述

但是请注意:你必须保证你的输出的格式是正确的,首先你输出的 P 必须不超过 k 且是非负整数,且无论你的方案正不正确,后面都要描述 P 条路径,例如你输出 P=2 但是只输出了一条路径的话,后面的其他组的输出可能就会被读入作为第二条路径,这会导致你得不到该有的分数

Sample Input

1
5 2
1 2
1 3
1 4
1 5

Sample Output

4
2
5 5
4 3

解析

这显然是一道树形\(dp\),我们可以先不考虑输出方案。

我们可以先设置简单的状态\(f[x][k]\)代表以\(x\)为根的子树中选了\(k\)条链的最大长度之和,但是我们发现好像不太适合从子树转移。

我们可以考虑一下从子树上转移过来会有几种情况,一个就是根节点\(x\)不包含在任何一条链中,还有就是根节点\(x\)包含在某个链中。

如果\(x\)包含在某个链中,还有两种情况:\(1.\) \(x\)现在是某个链的一端,也就是说,这条链还可以继续向上拓展。 \(2.\) \(x\)是某条链中间的一个点,也就是说有两条链在点\(x\)处合并了。

那样就可以设置状态了,\(f[x][k][0/1/2]\)就分别代表了如上的三种情况。

于是我们可以考虑合并,最简单的方式就是先不考虑这棵子树影响了根节点\(x\),把所有状态先暴力合并了,然后再考虑这棵子树向上延展,或者与其他链合并这两种影响根节点\(x\)的情况,单独转移。

这种复杂的树形\(dp\)由于没有显式地划分阶段,所以建议用滚动数组的方式转移,不建议直接转移,那样容易出现\(dp\)顺序的问题。

然后我们再考虑输出方案。一般的\(dp\)输出方案的方式都是记录每一个状态是从哪些状态转移而来的,但是显然这种背包类的\(dp\)不适合,他可能是由很多子节点共同转移过来的,直接记录空间是不够的。

但是我们考虑到一个节点顶多也就是给了他父亲一个贡献,所以我们可以换一种记录方式,把转移记录在子节点上。

记录了方案之后,我们就可以再通过一遍\(dfs\)输出了。当然,我们仍然需要对转移进行讨论,以便找到链的两个端点。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 10020 , K = 520 , INF = 0x3f3f3f3f;
inline int read(void)
{
int x = 0 , w = 0; char ch = ' ';
while ( !isdigit(ch) ) w |= ch == '-' , ch = getchar();
while ( isdigit(ch) ) x = x * 10 + ch - 48 , ch = getchar();
return w ? -x : x;
}
int n,k,f[N][K][3],dp[K][3],size[N];
vector < pair <int,int> > ans;
pair < int , int > fr[N][K][3];
vector < int > e[N];
inline void input(void)
{
n = read() , k = read();
for (int i=1;i<n;i++)
{
int x = read() , y = read();
e[x].push_back( y );
e[y].push_back( x );
}
}
inline bool upd(int &a,int b) { if ( b > a ) return a = b , true; return false; }
inline void merge(int x,int y)
{
memcpy( dp , f[x] , sizeof f[x] );
for (int i=0;i<=size[x];i++)
for (int j=0;j<=size[y]&&i+j<=k+1;j++)
{
if ( i + j <= k )
{
for (int a=0;a<3;a++)
for (int b=0;b<3;b++)
if ( upd( dp[i+j][a] , f[x][i][a] + f[y][j][b] ) )
fr[y][i+j][a] = make_pair( j , b );
if ( j > 0 )
if ( upd( dp[i+j][1] , f[x][i][0] + f[y][j][1] + 1 ) )
fr[y][i+j][1] = make_pair( j , -1 );
}
if ( i + j > 1 && j > 0 )
if ( upd( dp[i+j-1][2] , f[x][i][1] + f[y][j][1] ) )
fr[y][i+j-1][2] = make_pair( j , -1 );
}
size[x] += size[y];
memcpy( f[x] , dp , sizeof dp );
}
inline void dfs(int x,int fa)
{
for (int i=0;i<=k;i++)
for (int j=0;j<3;j++)
f[x][i][j] = -INF , fr[x][i][j] = make_pair(-INF,-INF);
size[x] = 1 , f[x][0][0] = 0;
f[x][1][1] = f[x][1][2] = 1;
for ( auto y : e[x] )
if ( y != fa )
dfs( y , x ) , merge( x , y );
}
inline int findpath(int x,int fa,int cnt,int op)
{
if ( !cnt ) return 0;
reverse( e[x].begin() , e[x].end() );
int _op,cur,c,t;
_op = op , cur = _op == 1 ? x : 0;
if ( op < 0 ) op = 1;
for ( int y : e[x] )
if ( y != fa )
{
if ( cnt == 0 ) break;
tie(c,t) = fr[y][cnt][op];
if ( t == -INF ) continue;
if ( t == -1 )
{
int ver = findpath( y , x , c , t );
if ( ver != 0 )
{
if ( cur )
ans.push_back( make_pair(cur,ver) ) , cur = 0;
else cur = ver;
}
cnt -= c - (--op);
}
else cnt -= c , findpath(y, x, c, t);
}
if ( _op > 0 && cur )
ans.push_back( make_pair(cur,cur) ) , cur = 0;
else if ( _op < 1 && !cur ) return x;
return cur;
}
inline void reset(void)
{
ans.clear();
for (int i=1;i<=n;i++)
e[i].clear();
}
int main(void)
{
int T; scanf("%d",&T);
ans.clear();
while ( T --> 0 )
{
input();
dfs( 1 , 0 );
int num = 0 , op = 0;
for (int i=1;i<=k;i++)
for (int j=0;j<3;j++)
if ( f[1][i][j] > f[1][num][op] )
num = i , op = j;
printf("%d\n",f[1][num][op]);
findpath( 1 , 0 , num , op );
printf("%d\n",ans.size());
for ( auto p : ans )
{
int x,y;
tie( x , y ) = p;
printf("%d %d\n",x,y);
}
reset();
}
return 0;
}

<后记>

『快乐链覆盖 树形dp』的更多相关文章

  1. 『战略游戏 最大利润 树形DP』

    通过两道简单的例题,我们来重新认识树形DP. 战略游戏(luoguP1026) Description Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要 ...

  2. 『没有上司的舞会 树形DP』

    树形DP入门 有些时候,我们需要在树形结构上进行动态规划来求解最优解. 例如,给定一颗\(N\)个节点的树(通常是无根树,即有\(N-1\)条无向边),我们可以选择任意节点作为根节点从而定义出每一颗子 ...

  3. 『大 树形dp』

    大 Description 滑稽树上滑稽果,滑稽树下你和我,滑稽树前做游戏,滑稽多又多.树上有 n 个节点,它们构成了一棵树,每个节点都有一个滑稽值. 一个大的连通块是指其中最大滑稽值和最小滑稽值之差 ...

  4. 『You Are Given a Tree 整体分治 树形dp』

    You Are Given a Tree Description A tree is an undirected graph with exactly one simple path between ...

  5. 『kamp 树形dp』

    kamp Description jz 市的云台山是个很美丽的景区,小 x 暑期到云台山打工,他的任务是开景区的大巴. 云台山景区有 N 个景点,这 N 个景点由 N-1 条道路连接而成,我们保证这 ...

  6. 『树上匹配 树形dp』

    树上匹配 Description 懒惰的温温今天上班也在偷懒.盯着窗外发呆的温温发现,透过窗户正巧能看到一棵 n 个节点的树.一棵 n 个节点的树包含 n-1 条边,且 n 个节点是联通的.树上两点之 ...

  7. 【bzoj1907】树的路径覆盖 树形dp

    题目描述 输入 输出 样例输入 1 7 1 2 2 3 2 4 4 6 5 6 6 7 样例输出 3 题解 树形dp 设f[x]表示以x为根的子树完成路径覆盖,且x为某条路径的一端(可以向上延伸)的最 ...

  8. (中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

    Problem Description   Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are ...

  9. 『保卫王国 树上倍增dp』

    保卫王国 Description Z 国有n座城市,n - 1条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻扎军队.驻扎军队需 ...

随机推荐

  1. SAP MM MIGO过账报错 - 用本币计算的余额 - 之对策

    SAP MM MIGO过账报错 - 用本币计算的余额 - 之对策 使用MIGO事务代码对采购订单4500000191,执行收货,系统报错: 详细错误信息如下: 用本币计算的余额 消息号 F5703 诊 ...

  2. FMDB的操作

    #import "ZYDataManager.h" #import "JSSportModel.h" FMDatabase *db = nil; @implem ...

  3. odoo12 如何设置超级用户

    在odoo12的版本中,和之前的版本有点不一样的地方 在odoo12版本之前,每个实例都是使用户名为Administrator的默认用户来创建的. 在数据库中user_id是1. 在代码中,你会发现 ...

  4. 学习10:Python重要知识

    Python易忽略知识 (1)print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end="": (2)isinstance 和 type 的区别在于:type()不 ...

  5. Rust自定义智能指针

    深了,真深了. use std::ops::Deref; struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) - ...

  6. Rust中的函数调用

    注意区别语句和表达式哟. Rust是一门基于表示式的语言,牢记!!! fn main() { println!("Hello world!"); another_function( ...

  7. 使用map将字数组里的对象重新组装

    变为数组  ["扬子","北京","上海海吉雅"] // 注意点 map循环的时候 不能够有空的 否则回出问题哦. var list= [{ ...

  8. 玩转淘宝SVN

    http://guojun2sq.blog.163.com/blog/static/64330861201492965059142/(参考) 三步走: 1.注册账号http://code.taobao ...

  9. Docker容器数据卷(七)

    Docker致力于: 将运用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的 容器之间希望有可能共享数据 Docker容器产生的数据,如果不通过docker co ...

  10. LG2463/BZOJ4698 「SDOI2008」Sandy的卡片 后缀数组

    问题描述 LG2463 BZOJ4698 题解 看到\(n\)个数串,一开始不太好处理,可以很容易想到把这\(n\)个数串连到一起,形成一个大串,但是每个串之间不容易处理. 经过思考,想到在每个串中间 ...