洛谷题目页面传送门 & 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的更多相关文章

  1. 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 ...

  2. Codeforces 29D Ant on the Tree 树的遍历 dfs序

    题目链接:点击打开链接 题意: 给定n个节点的树 1为根 则此时叶子节点已经确定 最后一行给出叶子节点的顺序 目标: 遍历树并输出路径.要求遍历叶子节点时依照给定叶子节点的先后顺序訪问. 思路: 给每 ...

  3. codeforces 704B - Ant Man 贪心

    codeforces 704B - Ant Man 贪心 题意:n个点,每个点有5个值,每次从一个点跳到另一个点,向左跳:abs(b.x-a.x)+a.ll+b.rr 向右跳:abs(b.x-a.x) ...

  4. 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 题意 给出一棵树,每条边上有一个字符,字符集大小只 ...

  5. codeforces 812E Sagheer and Apple Tree(思维、nim博弈)

    codeforces 812E Sagheer and Apple Tree 题意 一棵带点权有根树,保证所有叶子节点到根的距离同奇偶. 每次可以选择一个点,把它的点权删除x,它的某个儿子的点权增加x ...

  6. codeforces 220 C. Game on Tree

    题目链接 codeforces 220 C. Game on Tree 题解 对于 1节点一定要选的 发现对于每个节点,被覆盖切选中其节点的概率为祖先个数分之一,也就是深度分之一 代码 #includ ...

  7. Codeforces E. Alyona and a tree(二分树上差分)

    题目描述: Alyona and a tree time limit per test 2 seconds memory limit per test 256 megabytes input stan ...

  8. Codeforces 379 F. New Year Tree

    \(>Codeforces \space 379 F. New Year Tree<\) 题目大意 : 有一棵有 \(4\) 个节点个树,有连边 \((1,2) (1,3) (1,4)\) ...

  9. 【27.91%】【codeforces 734E】Anton and Tree

    time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

随机推荐

  1. SQL Server 存储过程相关语法

    一.定义变量及赋值 1.1 普通变量 --定义变量 declare @parameter_set int ) ) --set 关键字赋值 ; --select 赋值 select @parameter ...

  2. 用Python玩数据-笔记整理-第一章

    第一个程序:print >>>print("Hallo World!") >>>Hallo World! mystring = "Ha ...

  3. 微信小程序开发--组件(5)

    一.editor 富文本编辑器,可以对图片.文字进行编辑. 编辑器导出内容支持带标签的 html和纯文本的 text,编辑器内部采用 delta 格式进行存储. 通过setContents接口设置内容 ...

  4. 【题解】生日蛋糕-C++

    Description 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体.设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, ...

  5. [原创]JAVA解决喝汽水问题

    问题:一瓶汽水单价2.5元,四个瓶盖或者两个瓶子可以换取一瓶汽水.给定金额得出一共能喝几瓶汽水? 实现: #cat drink.java import java.io.BufferedReader; ...

  6. 【带着canvas去流浪(12)】用Three.js制作简易的MARVEL片头动画(上)

    目录 一. 大作业说明 二.基本思路 三.视频纹理表面修复--UV映射 3.1 问题描述 3.2 纹理贴图的基本原理-UV映射 3.3 关键示例代码 四.小结 示例代码托管在:http://www.g ...

  7. MsgWaitForMultipleObjects

    Use caution when calling the wait functions and code that directly or indirectly creates windows. If ...

  8. 使用SVG symbols建立图标系统完整指南

    从最开始的使用img图片,到后来的使用css sprite来减少服务器请求,再到流行的图形字体化图标Iconfont.现在,一种全新的图标使用方式开始流行了起来--SVG symbols图标. 工作原 ...

  9. struct模块(用于对象的压缩)

    6.27自我总结 struct模块 1.struct模块中的函数 函数 return explain pack(fmt,v1,v2-) string 按照给定的格式(fmt),把数据转换成字符串(字节 ...

  10. luogu题解 P5020 【货币系统 】

    思路 判断钱数是否可以转化为其他钱数的和 与楼下不同,我没有用sort.而是用了一个数组来特判. 思路其实只是简单dp. 详见代码. #include<cstdio> using name ...