洛谷题目页面传送门 & 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. 关于网页授权access_token和普通access_token的区别

    关于网页授权access_token和普通access_token的区别 1.微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授 ...

  2. Jenkins部署Web项目到远程tomcat

    1.填加插件Deploy to container Plugin. 2.在构建任务中填加构建后操作.并做如下配置: WAR/EAR files:是war包的相对路径,如target/xxx.war c ...

  3. NOIP 2004 虫食算题解

    问题 E: [Noip2004]虫食算 时间限制: 1 Sec  内存限制: 128 MB 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一 ...

  4. Python登录豆瓣并爬取影评

    上一篇我们讲过Cookie相关的知识,了解到Cookie是为了交互式web而诞生的,它主要用于以下三个方面: 会话状态管理(如用户登录状态.购物车.游戏分数或其它需要记录的信息) 个性化设置(如用户自 ...

  5. Excel催化剂开源第30波-在Excel上尽情地使用LINQ

    对于笔者这样的数据分析工作者来说,对数据库有较深的掌握,当然少不了对SQL查询的深度使用,如果在编程的世界中,可以复用这样的能力,真的是一件多么令人高兴的事情. 在.Net的世界中,恰恰提供了这样的能 ...

  6. C#3.0新增功能07 查询表达式

    连载目录    [已更新最新开发文章,点击查看详细] 查询是什么及其作用是什么 查询是一组指令,描述要从给定数据源(或源)检索的数据以及返回的数据应具有的形状和组织. 查询与它生成的结果不同. 通常情 ...

  7. 2017day1

    http://www.cnblogs.com/alex3714/articles/5465198.html 四.Python安装 windows 1 2 3 4 5 6 7 1.下载安装包     h ...

  8. MySql(Windows)

    百度云:链接:http://pan.baidu.com/s/1nvlSzMh      密码:o1cw 官网下载网址:http://dev.mysql.com/downloads/mysql/

  9. spark 源码分析之一 -- RDD的四种依赖关系

    RDD的四种依赖关系 RDD四种依赖关系,分别是 ShuffleDependency.PrunDependency.RangeDependency和OneToOneDependency四种依赖关系.如 ...

  10. linux初学者-DDNS配置篇

    linux初学者-DDNS配置篇 如果DNS服务器要记录多台主机的IP,且这些主机的IP都是通过DHCPD服务自动获取的,那么将会造成很大的困难,因为在DNS设置时无法得知主机具体的IP.如果DHCP ...