洛谷 P4665 [BalticOI 2015]Network
洛谷 P4665 [BalticOI 2015]Network
你有一棵 $ n $ 个节点的树,你可以在树上加一些边,使这棵树变成一张无重边、自环的图,且删掉任意一条边它仍然联通。求最少要加多少条边,要求输出方案。
$ solution: $
居然想出来了,但是代码十分囧长,写了一个小时。
- (删掉任意一条边图仍然联通)说明每一条边都至少属于一个环!这样删掉这条边后,它所连的两个节点可以通过环上另一条路径相连!
- 然后每一个叶子节点都必定会被连一条边!因为叶子节点本身只被一条边连向它父亲,这个叶子节点若不被加边,我们只要删去它和它父亲之间的那一条边,他就与树不连通了!
根据上面两个性质,我们不难想出一种加边方案(不一定最优):先随便指定一个根节点,然后从每个叶子节点向根节点连边,这样所有叶子节点向根节点的路径(一定包含树上所有路径)都至少属于一个环!
然后我们考虑怎么优化这个方案,首先根据性质2我们可以得出另一个结论:加边数目的下界是(叶子节点数除以2向上取整)。我们发现相比从每个叶子节点向根节点连边,如果我们存在两个叶子节点他们之间的简单路径包含根节点,我们只要将两个叶子节点连边,这条边一条可以顶两条边。于是我们考虑能不能将叶子节点两两匹配,使得边数优化成原来的一半,然后发现这不就是边数下界?我们可以让叶子节点两两匹配得到这个下界吗?
答案是显然的(好吧只是博主自己讲不清而已QAQ),首先自己画图可以发现,一定存在一个节点,它的儿子节点所含子树包含一些叶子节点,且包含叶子节点最多的那个儿子它所含叶子节点的数目小于总叶子节点数的一半!这个我们可以用二次扫描和换根法证明!
然后我们可以跑一遍树找到一个根节点,用一个优先队列维护根节点的儿子它们包含的叶子节点数,然后取出数目最大的两个儿子,将它们随便包含的一个叶子节点连边,然后删除两个叶子,再将两个儿子放回优先队列。这样不断重复,即可构造一种下界方案!
$ upd: $ 更新一种 $ O(n) $ 做法, 这种做法就是将上一段改一改即可,我们上一段优先队列讲了这么多,无非就是想让两个叶子节点不在同一个根的儿子内(就是要找到一种叶子节点的匹配方法,使得两个叶子节点之间的路径包括根节点)。现在有一种更简洁的算法:找到一个根节点(根节点的儿子的最大叶子节点数不超过总数的一半),这样我们再跑一边 $ dfs $ 找到整棵树的 $ dfs $ 序(序列里属于同一个儿子的叶子节点一定在一块),然后将序列从中间开始分开,第一个和 $ mid+1 $ 匹配,第二个和 $ mid+2 $ 匹配,以此类推,因为最大叶子节点数不超过总数的一半,这样匹配的两个叶子节点一定不在同一个儿子内!这样就能 $ O(n) $ 做完了!(这个对应后面的 $ code2 $ )
注意:洛谷的SPJ是有问题的,它会先判断所有连边是否都在叶子节点之间!
$ code1: $
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define db double
#define rg register int
using namespace std;
int n,rt,op;
int tot,top;
int a[500005];
int sz[500005];
int da[500005];
int qi[500005];
struct su{
int to,next;
}b[1000005];
int tou[500005];
struct pi{
int x,y;
inline bool operator <(const pi &z)const{
return x<z.x;
}
};
priority_queue<pi> q;
inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
if(sign)return -res; else return res;
}
inline void dfs(int i,int fa){ //找到根节点
rg big=0;
for(rg j=tou[i];j;j=b[j].next){
rg to=b[j].to;
if(to==fa)continue;
dfs(to,i); sz[i]+=sz[to];
big=max(big,sz[to]);
}big=max(big,tot-sz[i]);
if(big<=tot/2)rt=i;
}
inline void get(int i,int v,int fa){ //给所有叶子染色
if(a[i]==1){
da[i]=qi[v]; //链式前向星存储
qi[v]=i; sz[i]=1;
return ;
} sz[i]=0;
for(rg j=tou[i];j;j=b[j].next){
rg to=b[j].to;
if(to==fa)continue;
get(to,v,i); sz[i]+=sz[to];
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=qr(); op=qr();
for(rg i=1;i<n;++i){
rg x=qr(),y=qr(); ++a[x]; ++a[y];
b[++top]=su{y,tou[x]}; tou[x]=top;
b[++top]=su{x,tou[y]}; tou[y]=top;
if(a[x]>a[rt])rt=x; //找一个度数不为1的节点
}
for(rg i=1;i<=n;++i)
if(a[i]==1)++tot,sz[i]=1;
printf("%d\n",(tot+1)/2);
if(op==0)return 0;
dfs(rt,0);
for(rg i=tou[rt];i;i=b[i].next){
rg to=b[i].to; get(to,to,rt);
q.push(pi{sz[to],to}); //将根节点儿子和它所含叶子数目加入队列
}
while(!q.empty()){
pi x=q.top(); q.pop();
if(q.empty()){ //可能最后还剩一个叶子
printf("%d %d\n",qi[x.y],rt); //将它和根节点连即可
return 0;
}
pi y=q.top(); q.pop(); //取出含有叶子节点数最多的两个
printf("%d %d\n",qi[x.y],qi[y.y]); //将两个叶子连边
qi[x.y]=da[qi[x.y]]; qi[y.y]=da[qi[y.y]]; //删除两个叶子
if(--x.x>0)q.push(x); //将节点放回
if(--y.x>0)q.push(y);
}
return 0;
}
$ code2: $
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define db double
#define rg register int
using namespace std;
int n,rt;
int top,tt;
int a[500005];
int f[500005];
struct su{
int to,next;
}b[1000005];
int tou[500005];
inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
if(sign)return -res; else return res;
}
inline void dfs(int i,int fa){
if(a[i]==1){f[++tt]=i; return ;}
for(rg j=tou[i];j;j=b[j].next)
if(b[j].to!=fa) dfs(b[j].to,i);
}
int main(){ n=qr();
for(rg i=1;i<n;++i){
rg x=qr(),y=qr(); ++a[x]; ++a[y];
b[++top]=su{y,tou[x]}; tou[x]=top;
b[++top]=su{x,tou[y]}; tou[y]=top;
if(a[x]>1)rt=x; //找一个度数不为1的节点
} dfs(rt,0);
printf("%d\n",(tt+1)/2);
for(rg i=1;i<=tt/2;++i){
if(f[i]>f[tt/2+i])swap(f[i],f[tt/2+i]);
printf("%d %d\n",f[i],f[tt/2+i]);
} if(tt&1)printf("%d %d\n",f[tt/2],f[tt]);
return 0;
}
洛谷 P4665 [BalticOI 2015]Network的更多相关文章
- 洛谷 2679 [NOIP 2015] 子串
题目戳这里 一句话题意 给你两个字符串A,B从A中取出K个不重合子串(顺序与在A中顺序相同)组成B,问有多少种方案? Solution 话说重打还是出各种错误也是醉了 先看题目,因为答案与A串,B串和 ...
- 洛谷 P2678 [ NOIP 2015 ] 跳石头 —— 二分答案
题目:https://www.luogu.org/problemnew/show/P2678 二分答案. 代码如下: #include<iostream> #include<cstd ...
- 洛谷P3178[HAOI]2015 树上操作
题目 树剖裸题,这个题更可以深刻的理解树剖中把树上的节点转换为区间的思想. 要注意在区间上连续的节点,一定是在一棵子树中. #include <bits/stdc++.h> #define ...
- 洛谷 P4663 - [BalticOI 2008]魔法石(dp)
题面传送门 A:我该是有多无聊来写这种题的题解啊 B:大概是因为这题题解区里没有题解所以我来写一篇了,说明我有高尚的济世情怀(大雾 跑题了跑题了 首先看到字典序第 \(i\) 小小可以自然地想到按位决 ...
- 洛谷 P6573 [BalticOI 2017] Toll 题解
Link 算是回归OI后第一道自己写的题(考CSP的时候可没回归) 写篇题解纪念一下 题目大意: \(n\) 个点,\(m\) 条单向边,每条边的两端点 \(x\),\(y\)必定满足 \(\left ...
- 洛谷P2668 斗地主==codevs 4610 斗地主[NOIP 2015 day1 T3]
P2668 斗地主 326通过 2.6K提交 题目提供者洛谷OnlineJudge 标签搜索/枚举NOIp提高组2015 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 出现未知错误是说梗啊 ...
- POJ1236或洛谷2746或洛谷2812 Network of Schools
POJ原题链接 洛谷2746原题链接 洛谷2812(加强版)原题链接 显然在强连通分量里的所有学校都能通过网络得到软件,所以我们可以用\(tarjan\)求出强连通分量并缩点,统计缩点后每个点的入度和 ...
- 2018.10.30 一题 洛谷4660/bzoj1168 [BalticOI 2008]手套——思路!问题转化与抽象!+单调栈
题目:https://www.luogu.org/problemnew/show/P4660 https://www.lydsy.com/JudgeOnline/problem.php?id=1168 ...
- 洛谷P2812校园网络【Network of Schools加强版】
题目背景 浙江省的几所\(OI\)强校的神犇发明了一种人工智能,可以\(AC\)任何题目,所以他们决定建立一个网络来共享这个软件.但是由于他们脑力劳动过多导致全身无力身体被\(♂\)掏\(♂\)空,他 ...
随机推荐
- JS 替换所有的空格
在JS中替换掉输入框内的空格,是在处理表单需求的时候极为常用的一项操作,以防止用户的操作习惯引起数据异常,保证传参的安全性. NO.1 name.replace(" "," ...
- autoprefixer不起作用的坑
概述 今天同事说,nuxt.js的项目好像没有自动加前缀,我花了很长时间查找原因,最后终于发现,原来是没有加.browserslistrc文件...记录下来,供以后开发时参考,相信对其他人也有用. b ...
- windows下 文件资源管理器 的操作
alt + d 可以直接把光标移动到地址栏 shift + f10 可以触发右键, 后面就可以用键盘操作右键中的内容了 ( 如打开vscode alt + 空格 可以弹出窗口的菜单栏 ( 控制最大化 ...
- zabbix添加主机后无法显示解决
第一次添加主机后显示正常,后来删除了主机,重新添加了一下主机再也无法显示主机,很苦恼,原来需要点击重设,
- oracle 11g 数据库恢复技术 --rman catalog
Oracle RMAN的catalog并不是指标备份恢复操作的一个必要组件,但oracle推荐使用该组件.启用之后,归档日志.备份集.镜像复制等备份信息的保存地点是RMAN资料库(catalog), ...
- text_to_be_present_in_element
text_to_be_present_in_element(locator,text)是指定页面元素的文本位置, 一般用于验证一个文本信息或者错误的信息,我们任然以百度登录为案例, 用户名和密码为空, ...
- Chapter02 第二节 语句和变量
2.2 C++语句 2.11 声明语句和变量 示例程序: // carrots.cpp #include <bits/stdc++.h> using namespace std; int ...
- keras recall
# accuracy, fmeasure, precision,recall def mcor(y_true, y_pred): y_pred_pos = K.round(K.clip(y_pred, ...
- 2.proxychains----Macchanger----anonsurf
nano /etc/proxychains.conf 激活dynamic_chain 同是启用proxy_dns 只启用IP会泄露DNS位置信息,别人就知道你在用匿名 ipconfig ifco ...
- js 如何定义函数
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...