problem

洛谷链接

solution

这种要求值和恰好为 kkk 的题目,一般要先明确值和的取值范围。

所以我们先来确定一下值和的最小值和最大值。

将一条路径拆成若干条边,单独计算每条边对路径的贡献。

假设一条边将树划分成 S,TS,TS,T 集合。因为 nnn 为偶数,所以 S,TS,TS,T 同奇偶。

则贡献最大值就是每条路径都经过该边,最小值就是尽量内部消化。

每个点只能匹配一个点,故最大值为 min⁡(∣S∣,∣T∣)\min(|S|,|T|)min(∣S∣,∣T∣),最小值为 ∣S∣&1|S|\& 1∣S∣&1。

这个 min⁡\minmin 我们想办法去掉,∣S∣+∣T∣=n⇒min⁡(∣S∣,∣T∣)≤n2|S|+|T|=n\Rightarrow \min(|S|,|T|)\le \frac n 2∣S∣+∣T∣=n⇒min(∣S∣,∣T∣)≤2n​。

啊——重心。其定义就是任意一个亲儿子子树的大小都满足 siz[x]≤n−siz[x]siz[x]\le n-siz[x]siz[x]≤n−siz[x]。

即值和最大值就是尽量所有路径都过重心,i↔i+n2i\leftrightarrow i+\frac n 2i↔i+2n​ dfn\text{dfn}dfn 序配对。

不妨从最大值考虑下放成 kkk。

如图,假设原先是 1↔v,2↔u⇒1↔2,u↔v1\leftrightarrow v,2\leftrightarrow u\Rightarrow 1\leftrightarrow 2,u\leftrightarrow v1↔v,2↔u⇒1↔2,u↔v,这样子就做到了对答案减少 2∗dep[lca(u,v)]2*dep[lca(u,v)]2∗dep[lca(u,v)] 的效果。

还发现,每次贡献的改变都是偶数,所以如果 kkk 与最大值不同奇偶也是无解的。

像倍增一样的,从大往小放缩。

找到一个深度最深的且子树大小(含自己)至少为 222 的点。

先考虑让这个点的子树内产生一对内部匹配,那么减少的贡献就是最大的。

  • 如果减少这个贡献,还没有下放到 kkk,就继续上面的操作。
  • 如果下放过了,因为路径肯定是连续的,那么从重心到这个点深度层中一定恰好有一个点能恰好使得最后下放到 kkk。

具体而言,构造如下:

  • k=Maxk=Maxk=Max,直接 dfn\text{dfn}dfn 序后,i↔i+n2i\leftrightarrow i+\frac n 2i↔i+2n​。

  • k<Maxk<Maxk<Max。假设重心为 rootrootroot。

    取 rootrootroot 亲儿子中子树大小最大的点 xxx,则 siz[x]≥2siz[x]\ge 2siz[x]≥2。

    取 xxx 后代中最深的且子树大小至少为 222 的点 yyy。

    • 2dep[y]<Max−k2dep[y]< Max-k2dep[y]<Max−k,可以选择两个 lca=ylca=ylca=y 的点内部匹配并删除,MaxMaxMax 和 kkk 的差距缩小 2dep[y]2dep[y]2dep[y],回到上一步。

    • 2dep[y]≥Max−k2dep[y]\ge Max-k2dep[y]≥Max−k,这一条路径上一定有一个点 z,dep[z]=Max−k2z,dep[z]=\frac{Max-k}{2}z,dep[z]=2Max−k​,同样选择两个 lca=zlca=zlca=z 的点内部匹配。break 整个过程。

      set 存子树内的深度及点编号,到时候一个 lower_bound 查即可。

    当然这个点可以是 y,zy,zy,z 本身。

过程中的匹配变化,k<Max,2dep[y]≤Max−kk<Max,2dep[y]\le Max-kk<Max,2dep[y]≤Max−k,不会让重心移动,所以不用每次都重新求,set 模拟维护。

具体而言,算法流程如下:

  • dfs1 获得重心以及值和的上下界。迅速特判 kkk 能够被构造出来。

  • 对重心的每个亲儿子分别进行子树 dfs2s[] 记录子树内每个点的深度,子树大小,未配对儿子个数,隶属于重心的亲儿子的编号。深度为第一关键字。

    只有这个亲儿子子树大小 ≥2\ge 2≥2 的时候,才能用于后面的匹配下放,才放入候选方案 g 中。子树大小为第一关键字。

  • 从最大亲儿子 xxx 开始考虑,用 lower_bound 找构造中的 yyy,并进行判断。

    进行信息修正。xxx 子树中能匹配的个数减少 2,siz[x]−=22,siz[x]-=22,siz[x]−=2。随便找两个 yyy 内未匹配的点配对输出,打上已配对标记。

    重复本条流程直到跳出。

  • 剩下的未匹配点就是普通的 i↔i+n2i\leftrightarrow i+\frac n 2i↔i+2n​ 配对,dfs3 找所有未匹配点(这些点可能已经被一些匹配点割裂成不联通的几个部分了)。

code

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100005
set < pair < int, int > > s[maxn], g;
vector < int > G[maxn], id;
int n, k;
int center = 1e18, root, Min, Max;
bool vis[maxn];
int f[maxn], son_cnt[maxn], siz[maxn], dep[maxn], belong[maxn];void dfs1( int u, int fa ) {int maxson = 0; siz[u] = 1;for( int v : G[u] ) {if( v == fa ) continue;dfs1( v, u );siz[u] += siz[v];maxson = max( maxson, siz[v] );}maxson = max( maxson, n - siz[u] );if( maxson < center ) center = maxson, root = u;if( u ^ 1 ) Min += siz[u] & 1, Max += min( siz[u], n - siz[u] );
}void dfs2( int u, int fa, int t ) {belong[u] = t, dep[u] = dep[fa] + 1, siz[u] = 1, f[u] = fa;for( int v : G[u] ) {if( v == fa ) continue;else dfs2( v, u, t );son_cnt[u] ++, siz[u] += siz[v];}if( son_cnt[u] ) s[t].insert( make_pair( dep[u], u ) );
}void dfs3( int u, int fa ) {if( ! vis[u] ) id.push_back( u );for( int v : G[u] ) if( v ^ fa ) dfs3( v, u );
}void _delete( int x ) {vis[x] = 1;son_cnt[f[x]] --;if( ! son_cnt[f[x]] ) s[belong[f[x]]].erase( make_pair( dep[f[x]], f[x] ) );
}void match( int x ) {vector < int > use;for( int i : G[x] ) if( i ^ f[x] and ! vis[i] ) use.push_back( i );if( ! vis[x] ) use.push_back( x );printf( "%lld %lld\n", use[0], use[1] );_delete( use[0] ), _delete( use[1] );
}signed main() {scanf( "%lld %lld", &n, &k );for( int i = 1, u, v;i < n;i ++ ) {scanf( "%lld %lld", &u, &v );G[u].push_back( v );G[v].push_back( u );}dfs1( 1, 0 );if( k < Min or Max < k or ( Max - k ) & 1 ) return ! printf( "NO\n" );else printf( "YES\n" );for( int x : G[root] ) {dfs2( x, root, x );if( siz[x] > 1 ) g.insert( make_pair( siz[x], x ) );}int rest = Max - k;while( rest ) {int x = g.rbegin() -> second;g.erase( make_pair( siz[x], x ) );int y = s[x].rbegin() -> second;if( ( dep[y] << 1 ) >= rest ) {y = s[x].lower_bound( make_pair( rest >> 1, 0 ) ) -> second;match( y );break;}else {rest -= ( dep[y] << 1 );siz[x] -= 2;match( y );if( siz[x] > 1 ) g.insert( make_pair( siz[x], x ) );}}dfs3( root, 0 );for( int i = 0;i < ( id.size() >> 1 );i ++ )printf( "%lld %lld\n", id[i], id[i + ( id.size() >> 1 )] );return 0;
}

CodeForces 1396E Distance Matching(构造+树的重心+dfs+set)相关推荐

  1. 树的重心——DFS求解

    定义: 树的重心也叫树的质心.对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小.换句话说,删除这个点后最大连通块(一定是树)的结点数最小. 性质: 树中所 ...

  2. POJ 1655 Balancing Act (树的重心 + DFS)

    传送门:POJ 1655 题目大意: 求树的重心,如果有多个重心则输出根节点编号最小的一个.树的重心也叫树的质心.找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后 ...

  3. CodeForces - 1476E Pattern Matching(字典树+拓扑)

    题目链接:点击查看 题目大意:给出 nnn 个模式串和 mmm 个匹配串,题目要求输出一种模式串的排列方式,使得 mmm 个模式串从头开始匹配的话,可以匹配到相应的模式串 模式串的长度不超过 444, ...

  4. 点分治学习:树的重心(质心)

    树的重心定义: 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心 dfs一遍就可以找到树的重心void dfs(int u,int fa) {//vis[u]=true,si ...

  5. 2019CCPC-江西省赛 A-Cotree(树的重心)

    链接:JXCPC A-Cotree Avin has two trees which are not connected. He asks you to add an edge between the ...

  6. 模板 - 树上问题(树的直径、动态查询树的直径、树的重心)

    整理的算法模板合集: ACM模板 目录 一.树的直径 树形DP 两次DFS / BFS(找到直径的两个端点) 二.动态修改树的边权并求每个时刻的直径(线段树) 三.树的重心 一.树的直径 树的直径满足 ...

  7. 【树形DP】树的重心详解+多组例题详解

    目录 定义: 性质: 算法分析: POJ 1655 Balancing Act(求重心) POJ 3107 Godfather P1364 医院设置(树形DP) 定义: 树的重心也叫树的质心.对于一棵 ...

  8. 树形dp——树的重心(2) 代码调试理解

    和树的最大独立问题类似,先任选一个结点作为根节点,把无根树变成有根树,然后设d(i)表示以i为根的子树的结点的个数.不难发现d(i)=∑d(j)+1,j∈s(i).s(i)为i结点的所有儿子结点的编号 ...

  9. POJ-1655 Balancing Act 树的重心

    题意:完全符合树的重心:即找到一个点,其所有的子树中最大的子树节点最少. 代码如下: #include <cstdlib> #include <cstring> #includ ...

最新文章

  1. bat 两个文本字符替换_数据人必会的Excel|掌握这些文本函数,让你的工作如鱼得水...
  2. package.json中dependencies 与devDependencies 的区别
  3. BSS段,数据段,代码段,堆内存和栈
  4. container 的背后
  5. CodeForces - 76E Points
  6. 黑苹果系统坏了如何恢复_黑苹果macOS系统U盘版/恢复版基础安装教程
  7. 联想超融合平台oracle,联想AIO超融合云一体机解决方案.pdf
  8. 拼多多否认对极兔快递“政策倾斜”;86版西游记“红孩儿”成中科院博士;AirTag遭破解 | 极客头条...
  9. Android-广播
  10. E WORD 0410
  11. linux中 」 、」」 的用法
  12. layui监听radio点击事件
  13. 谷歌浏览器配置微信浏览器_微信网页版 - Chrome社交与通讯插件 - 画夹插件网
  14. 使用React Native可以开发Window桌面应用了!
  15. uni-app 使用 @touchmove.stop.prevent 导致弹框内容无法滚动
  16. linux服务器console口,Linux重定向console口控制台
  17. 深圳水上乐园有哪些 而且便宜还好玩
  18. 升级glibc经验谈!!!
  19. David Cutler NT之父
  20. 语音识别相关资料整理

热门文章

  1. 我国最新十大黑科技发布,颠覆你的想象!
  2. 将历史、数学、语文、地理、政治知识融会贯通的诀窍就是它
  3. 进军人工智能,数学基础很重要?
  4. mysql utf8 bin设置_[mysql]修改collation为utf8_bin
  5. io python 读取pdf_Python读取PDF文件--pdfminer
  6. 12对胸椎对应体表标志_铁路信号之信号表示器及标志(三)
  7. 通过python实现linux切换用户_Python操作远程服务器切换到root用户
  8. python opencv 图像添加噪声_opencv+python同时加椒盐噪声和随机杂点噪声
  9. php大马源码 手机网页,php大马源码:【百家号】脸书百科,分析 PHP大马-php_mof SHELL Web程序...
  10. Spring框架相关问题