CodeForces 29D Ant on the Tree
洛谷题目页面传送门 & CodeForces题目页面传送门
题意见洛谷里的翻译。
这题有\(3\)种解法,但只有\(1\)种是正解(这不是废话嘛)。
方法\(1\):最近公共祖先LCA(正解)
真的把它当作一棵树来做。使用父亲表示法,记录每个节点的父亲。可是输入中只能告诉你谁和谁连,并没有说谁是谁的父亲,这该怎么办呢?其实很简单,只需要通过根是\(1\)这个信息,先把\(1\)的父亲设成自己,把所有\(1\)的邻居\(i\)的父亲都设成\(1\),然后再对\(i\)进行如下操作:把所有\(i\)的“还没有父亲”的邻居\(j\)的父亲都设成\(i\),然后对\(j\)进行如下操作:把所有\(j\)的“还没有父亲”的邻居\(k\)的父亲都设成\(j\),然后对\(k\)进行如下操作……这样就做出来一棵树。我们还需要一个二维bool
数组,在\(\operatorname{O}\left(n^2\right)\)时间内预处理出对于任意一对节点与叶子节点(也就是说第一维是任意节点,第二维是叶子节点)\((x,y)\),\(x\)是不是\(y\)的祖先。设叶子节点集合为\(l\),然后从\(1\)走到\(l_1\)、从\(l_1\)走到\(l_2\)、……、从\(l_{n-1}\)走到\(l_n\)、从\(l_n\)走到\(1\)。对于每一次走,从起点一直向上走,一直走到是终点的祖先为止(相当于走到原起点和终点的最近公共祖先(LCA)),再向下走到终点,把经过的点都压入答案序列。最后如果答案序列的大小不是\(2n-1\)就输出\(-1\),否则输出答案就可以了。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> nei[301]/*邻接表*/,ans/*答案序列*/;
int f[301];//父亲
bool ance[301][301];//[1]是否是[2]的祖先
void mktre(int x){//对x进行如下操作:
for(int i=0;i<nei[x].size();i++)//将所有x的
if(!f[nei[x][i]])/*“孤儿”邻居*/f[nei[x][i]]=x/*收养*/,mktre(nei[x][i])/*并对它进行如下操作……*/;
}
void go(int st,int ed){//从st走到ed
while(!ance[st][ed])st=f[st],ans.push_back(st);//往上走到是ed的祖先为止
vector<int> rev;
while(st!=ed)rev.push_back(ed),ed=f[ed];//本应向下走,但用的是父亲表示法,只能向上走
for(int i=rev.size()-1;i>=0;i--)ans.push_back(rev[i]);//倒过来压入答案序列
}
int main(){
int n/*节点数*/,i;scanf("%d",&n);
for(i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
nei[x].push_back(y);nei[y].push_back(x);
}
f[1]=1;/*1的父亲是自己*/mktre(1);//先对1进行操作
int x=1,y;ance[1][1]=true;
ans.push_back(1);//因为1没有机会压入答案序列,只好特殊招待
while(~scanf("%d",&y)){
int z=y;while(z!=1)ance[z][y]=true,z=f[z];ance[1][y]=true;//预处理ance
go(x,y);x=y;//从l[i]走到l[i+1]
}
go(x,1);
if(ans.size()>(n<<1)-1)return !printf("-1");//大小不是2n-1
for(i=0;i<ans.size();i++)printf("%d ",ans[i]);//输出答案
return 0;
}
这种方法的时间复杂度是\(\mathrm O\!\left(n^2\right)\)。因为做树的时间是边数,\(\mathrm{O}(n)\);走一次的时间是\(\mathrm{O}(n)\),走\(n\)次\(\mathrm O\!\left(n^2\right)\)。对于\(300\)的水数据,简直再容易不过了!
方法\(2\):暴搜
不把这张图当作树来看,而当作图。走的时候,从起点毫无方向感地搜遍全图直到搜到终点为止。走的函数里要再加一个参数,表示走过来的节点,避免再回去,造成死循环。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> nei[301]/*邻接表*/,ans/*答案序列*/;
bool dfs(int st/*起点*/,int ed/*终点*/,int prv/*走过来的,避免死循环*/){//暴搜
if(st==ed)return true;//到达了,带回这个喜讯
for(int i=0;i<nei[st].size();i++)//枚举邻居
if(nei[st][i]!=prv&&dfs(nei[st][i],ed,st)){//如果不是走过来的,那看看能不能搜到终点
ans.push_back(nei[st][i]);//此时已经搜到了,压入答案序列
return true;//搜到了,返回
}
return false;//没搜到
}
int main(){
int i,n/*节点数*/;scanf("%d",&n);
for(i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
nei[x].push_back(y);nei[y].push_back(x);
}
int x=1,y;
while(~scanf("%d",&y))dfs(y,x,0),x=y;//从l[i]开始暴搜l[i+1],因为dfs中是回溯时压入答案序列的,是反的,所以起点和终点也要反过来,反反得正
dfs(1,x,0);
ans.push_back(1);//因为1没有机会压入答案序列,只好特殊招待
if(ans.size()>(n<<1)-1)return !printf("-1");
for(i=0;i<ans.size();i++)printf("%d ",ans[i]);
return 0;
}
暴搜中枚举邻居,每个邻居都可能将整个图遍历一遍,\(\mathrm O\!\left(n^2\right)\),最多有\(n\)次暴搜,所以整个时间复杂度是\(
\mathrm O\!\left(n^3\right)\)。这时间复杂度是在欺负出题人的数据范围吗?
方法\(3\):Floyd指路
聪明的读者也许一定没想到,这题还可以用Floyd吧!先用Floyd算出任意两点的最短路(邻居距离为\(1\)),然后在走的时候,就有方向、不盲目、不彷徨、自信了,很显然,走能够缩短与终点的距离的邻居呗!在这里,Floyd起到了指路的作用。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f//设成INT_MAX相加时会爆int
int dis[301][301]/*最短距离*/,n/*节点数*/;
vector<int> ans;//答案序列
void go(int st,int ed){//从st走到ed
if(st==ed)return;//到达,返回
for(int i=1;/*一定有能走的点,所以不需要终止条件*/;i++)//枚举每个点
if(dis[st][i]==1/*是邻居*/&&dis[i][ed]<dis[st][ed]/*能缩短距离*/){//走
ans.push_back(i);//压入答案序列
go(i,ed);//走一步
return;//能走的点只有一个,找到了就算成功了,不再找了
}
}
int main(){
int i,j;scanf("%d",&n);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)dis[i][j]=i==j?0:inf;//初始化dis
for(i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
dis[x][y]=dis[y][x]=1;//邻居的距离为1
}
//Floyd
for(int k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
// for(i=1;i<=n;i++){for(j=1;j<=n;j++)printf("dis[%d][%d]=%d\t",i,j,dis[i][j]);puts("");}
int x=1,y;
ans.push_back(1);//因为1没有机会压入答案序列,只好特殊招待
while(~scanf("%d",&y))go(x,y),x=y;//从l[i]走到l[i+1]
go(x,1);
if(ans.size()>(n<<1)-1)return !printf("-1");
for(i=0;i<ans.size();i++)printf("%d ",ans[i]);
return 0;
}
虽然有方向了,但也要为此付出代价——奇慢无比的Floyd。所以时间复杂度还是\(\mathrm O\!\left(n^3\right)\)。题目被虐了,出题人好可怜
CodeForces 29D Ant on the Tree的更多相关文章
- codeforces 29D Ant on the Tree (dfs,tree,最近公共祖先)
D. Ant on the Tree time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- Codeforces 29D Ant on the Tree 树的遍历 dfs序
题目链接:点击打开链接 题意: 给定n个节点的树 1为根 则此时叶子节点已经确定 最后一行给出叶子节点的顺序 目标: 遍历树并输出路径.要求遍历叶子节点时依照给定叶子节点的先后顺序訪问. 思路: 给每 ...
- codeforces 704B - Ant Man 贪心
codeforces 704B - Ant Man 贪心 题意:n个点,每个点有5个值,每次从一个点跳到另一个点,向左跳:abs(b.x-a.x)+a.ll+b.rr 向右跳:abs(b.x-a.x) ...
- codeforces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(启发式合并)
codeforces 741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths 题意 给出一棵树,每条边上有一个字符,字符集大小只 ...
- codeforces 812E Sagheer and Apple Tree(思维、nim博弈)
codeforces 812E Sagheer and Apple Tree 题意 一棵带点权有根树,保证所有叶子节点到根的距离同奇偶. 每次可以选择一个点,把它的点权删除x,它的某个儿子的点权增加x ...
- codeforces 220 C. Game on Tree
题目链接 codeforces 220 C. Game on Tree 题解 对于 1节点一定要选的 发现对于每个节点,被覆盖切选中其节点的概率为祖先个数分之一,也就是深度分之一 代码 #includ ...
- Codeforces E. Alyona and a tree(二分树上差分)
题目描述: Alyona and a tree time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- Codeforces 379 F. New Year Tree
\(>Codeforces \space 379 F. New Year Tree<\) 题目大意 : 有一棵有 \(4\) 个节点个树,有连边 \((1,2) (1,3) (1,4)\) ...
- 【27.91%】【codeforces 734E】Anton and Tree
time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
随机推荐
- SQL Server 存储过程相关语法
一.定义变量及赋值 1.1 普通变量 --定义变量 declare @parameter_set int ) ) --set 关键字赋值 ; --select 赋值 select @parameter ...
- 用Python玩数据-笔记整理-第一章
第一个程序:print >>>print("Hallo World!") >>>Hallo World! mystring = "Ha ...
- 微信小程序开发--组件(5)
一.editor 富文本编辑器,可以对图片.文字进行编辑. 编辑器导出内容支持带标签的 html和纯文本的 text,编辑器内部采用 delta 格式进行存储. 通过setContents接口设置内容 ...
- 【题解】生日蛋糕-C++
Description 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体.设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, ...
- [原创]JAVA解决喝汽水问题
问题:一瓶汽水单价2.5元,四个瓶盖或者两个瓶子可以换取一瓶汽水.给定金额得出一共能喝几瓶汽水? 实现: #cat drink.java import java.io.BufferedReader; ...
- 【带着canvas去流浪(12)】用Three.js制作简易的MARVEL片头动画(上)
目录 一. 大作业说明 二.基本思路 三.视频纹理表面修复--UV映射 3.1 问题描述 3.2 纹理贴图的基本原理-UV映射 3.3 关键示例代码 四.小结 示例代码托管在:http://www.g ...
- MsgWaitForMultipleObjects
Use caution when calling the wait functions and code that directly or indirectly creates windows. If ...
- 使用SVG symbols建立图标系统完整指南
从最开始的使用img图片,到后来的使用css sprite来减少服务器请求,再到流行的图形字体化图标Iconfont.现在,一种全新的图标使用方式开始流行了起来--SVG symbols图标. 工作原 ...
- struct模块(用于对象的压缩)
6.27自我总结 struct模块 1.struct模块中的函数 函数 return explain pack(fmt,v1,v2-) string 按照给定的格式(fmt),把数据转换成字符串(字节 ...
- luogu题解 P5020 【货币系统 】
思路 判断钱数是否可以转化为其他钱数的和 与楼下不同,我没有用sort.而是用了一个数组来特判. 思路其实只是简单dp. 详见代码. #include<cstdio> using name ...