[NOIP提高组2018day2t1]旅行
题目描述
给定n个城市,m条双向道路的图, 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些道路从一个城市前往另一个城市。
小 Y 的旅行方案是这样的:任意选定一个城市作为起点,然后从起点开始,每次可 以选择一条与当前城市相连的道路,走向一个没有去过的城市,或者沿着第一次访问该 城市时经过的道路后退到上一个城市。当小 Y 回到起点时,她可以选择结束这次旅行或 继续旅行。需要注意的是,小 Y 要求在旅行方案中,每个城市都被访问到。(注意:同一条道路不能走两遍,也就是回头路只能走一次)
为了让自己的旅行更有意义,小 Y 决定在每到达一个新的城市(包括起点)时,将 它的编号记录下来。她知道这样会形成一个长度为 nn 的序列。她希望这个序列的字典序 最小,你能帮帮她吗? 对于两个长度均为 n 的序列 A和 B,当且仅当存在一个正整数 x,满足以下条件时, 我们说序列 A 的字典序小于 B。
- 对于任意正整数 1≤i<x,序列A的第 i 个元素 Ai 和序列 B 的第 i 个元素 Bi 相同。
- 序列 A 的第 x 个元素的值小于序列 B 的第 x 个元素的值。
题目大意
给你一个图,让你找到字典序最小的访问顺序。
解法
因为图近似一棵树,对于前15组数据,是一棵树,我们就只需要每次按照当前节点子节点从小到大的顺序来排列就可以了。(这个东西应该是贪心)
那么对于接下来的几组数据,因为多了一条边,那么就一定会形成环,我们就每一次拆掉一条边,做和前面的做法一样的就可以了。
AC代码
#include<bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define N 5005
#pragma GCC optimize(2)
using namespace std;
struct edge{
int u,to;
}E[N<<1];
vector<int>G[N];
int ans[N],H[N];
int n,m,tot,cnt,sign1,sign2;
bool vis[N];
int read(){
int w=0,x=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return w?-x:x;
}
void dfs(int u,int fa){
if(vis[u])return;
vis[u]=1;
ans[++tot]=u;
for(int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if(v==fa||(v==sign1&&u==sign2)||(v==sign2&&u==sign1)) continue;
dfs(v,u);
}
}
bool judge(int *a,int *b){
for(int i=1;i<=n;i++) {
if(a[i]==b[i]) continue;
else return a[i]<b[i];
}
return true;
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
G[u].pb(v);
G[v].pb(u);
E[++cnt]=(edge){u,v};
E[++cnt]=(edge){v,u};
}
for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
if(m==n-1){
ms(vis,0);
tot=0;
dfs(1,-1);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
else{
for(int i=1;i<=cnt;i+=2){
sign1=E[i].u,sign2=E[i].to;
ms(vis,0); tot=0; int res[N]; for(int j=1;j<=n;j++) res[j]=ans[j];
dfs(1,-1);
if(tot==n){
if(res[1]==0) continue;
if(judge(res,ans)) for(int j=1;j<=n;j++) ans[j]=res[j];
}
else for(int j=1;j<=n;j++) ans[j]=res[j];
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
return 0;
}
nlogn的做法
\(update / by / 2019/3/1\)
虽然能\(n^2\)过百万,但是我们还是有必要研究一下基环树这个玩意的。
基环树就是在一棵树上加了一条边从而变成了带环的树。
自己在做题的时候一个不小心乱搞\(yy\)了一种基环树的做法(可能就是原来的做法,但是我在做的时候没有看过题解,纯属瞎蒙):我们从1开始递归,那么我们一边标记一边递归,如果递归到了第一个有标记的节点,那么这个节点一定是我们环上的入点,也就是第一次进入环的节点,证明略。那么我们就在这个过程中记录父亲,最后在将入点的父亲变成我们遍历的倒数第二个节点就可以了。这样子我们就将所有的环内的节点的父亲穿在了一起,也就是说我们可以通过用父亲来遍历整个环。
以上就是我自己yy出的查环做法。
先给出查询的代码:
void find_in_point(int u,int ft){
if (vis[u]){if(fg){fg=0;st=u;fa[u]=ft;return;}else return;}
fa[u]=ft; vis[u]=1;
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (v==ft) continue;
find_in_point(v,u);
}
}
void get_loop(int u,int g){
loop[++sum]=u;
flg[u]=1;
if (fa[u]==g) return;
get_loop(fa[u],g);
}
接下来就是O(n)或者是O(nlogn)查环,我做了两种方法,不过有一种方法有bug在你谷测评只有96分,巨难受qwq,如果有知道为什么会错的小伙伴请在下方留言或者私信,谢谢,下方给出96分代码。(不要吐槽沃,我太菜了)
# include<bits/stdc++.h>
# define ms(a,b) memset(a,b,sizeof(a))
# define pb push_back
# define inf 0x7f7f7f7f
# define LL long long
# define N 500005
using namespace std;
vector<int>G[N];
int fa[N],ans[N],loop[N],nxt[N],mxs[N],clo[N];
bool vis[N],fg,flg[N];
int st,n,m,s1,s2,tot,sum,num;
inline int gi(){
int w=0,x=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return w?-x:x;
}
void dfs(int u,int fa){
ans[++tot]=u;
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (v==fa||(s1==u&&s2==v)||(s1==v&&s2==u)) continue;
dfs(v,u);
}
}
void find_in_point(int u,int ft){
if (vis[u]){if(fg){fg=0;st=u;fa[u]=ft;return;}else return;}
fa[u]=ft; vis[u]=1;
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (v==ft) continue;
find_in_point(v,u);
}
}
void get_loop(int u,int g){loop[++sum]=u;flg[u]=1;if (fa[u]==g) return;get_loop(fa[u],g);}
int find(int u,int sta){
int res=inf;
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (v>sta&&!flg[v]) res=min(res,v);
}
return (res==inf)?0:res;
}
void init(int u,int ft,int cl){
clo[u]=cl;
if (u==st){
int nt=inf,cnt=0;
for (int i=0;i<(int)G[u].size();i++){int v=G[u][i]; if (flg[v]) nt=min(nt,v),num=max(num,v); else mxs[u]=max(mxs[u],v);}
mxs[u]=cnt; nxt[u]=nt; if (cnt>=2) init(nt,u,-1); else init(nt,u,-1); return;
}
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (v==st||v==ft) continue;
if (!flg[v]) mxs[u]=max(mxs[u],v);
}
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (!flg[v]||v==ft) continue;
nxt[u]=v;
if (v==st) continue;
if (mxs[u]!=-1) init(v,u,u); else init(v,u,cl);
}
}
void cut(int u,int fa){
if (u==st){cut(nxt[u],u);return;}
int v=nxt[u],tmp=-inf;
if (v==st||v==0){s1=u;s2=st;return;}
if (clo[u]!=-1) tmp=find(clo[u],u);
if (v<mxs[u]||v<tmp||v<u||(num>v&&clo[u]==-1&&mxs[u]!=0)) cut(v,u);
else{s1=u;s2=v;return;}
}
int main(){
n=gi(),m=gi();
for (int i=1;i<=m;i++){
int u=gi(),v=gi();
G[u].pb(v); G[v].pb(u);
}
for (int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
s1=s2=-1;
if (!(n^m)){
ms(vis,0); ms(flg,0); fg=1; find_in_point(1,0);
sum=0; get_loop(st,st);
ms(mxs,-1); ms(clo,-1); mxs[st]=0; init(st,0,-1);
cut(st,0);
}
dfs(1,0);
if (ans[10]!=0) for (int i=1;i<=n;i++) printf("%d ",ans[i]); else for (int i=1;i<=n;i++) printf("0 ");
return 0;
}
一些自己做的数据(对拍调试时自己错误的一些部分)
10 10
1 2
2 7
7 5
5 6
6 4
4 8
8 1
8 10
3 4
3 9
ans=1 2 7 5 6 4 3 9 8 10
10 10
1 3
2 4
3 5
4 3
5 9
6 1
7 2
8 5
9 1
10 1
ans=1 3 4 2 7 5 8 6 9 10
10 10
1 5
2 5
3 2
4 2
5 9
6 7
7 5
8 2
9 3
10 2
ans=1 5 2 3 4 8 10 7 6 9
10 10
5 10
4 1
6 2
1 7
9 2
3 8
7 6
9 4
2 10
2 3
ans=1 4 7 6 2 3 8 9 10 5
nlogn做法ac代码
做法:因为我们的回溯是有条件的,那么我们必须要判断如果回溯能否最优,那么我们就考虑当前的回溯能否是答案更优,因为这是字典序最小,所以我们的答案只有一个,肯定满足某个特殊的单调性,那么我们就递归搜索当前节点的子节点中最小的能否满足当前的决策的答案,将这个标记一直传递下去。
# include<bits/stdc++.h>
# define ms(a,b) memset(a,b,sizeof(a))
# define pb push_back
# define inf 0x7f7f7f7f
# define LL long long
# define N 500005
using namespace std;
vector<int>G[N];
int fa[N],ans[N],to[N<<2],S[N],s[N],de[N];
bool flg[N],vis[N];
int n,m,cir,cnt,sum,top,len;
inline int gi(){
int w=0,x=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return w?-x:x;
}
void solve(){
memset(flg,1,sizeof(flg));
for (int i=1;i<=n;i++) flg[i]=true;
top=0;
for (int i=1;i<=n;i++){
if (de[i]==1) S[++top]=i;
}
while(top){
int u=S[top--];
flg[u]=0,cnt--;
for (int j=0;j<(int)G[u].size();j++){
int v=G[u][j];
de[v]--;
if (de[v]==1) S[++top]=v;
}
}
}
void dfs(int u,int last,int fa){
if(!vis[u]){
ans[++sum]=u;
if (flg[u]) cnt--;
}
vis[u]=1;
int l=len+1,r=len;
for (int i=0;i<(int)G[u].size();i++){
int v=G[u][i];
if (!vis[v]) to[++len]=v;
}
if (len<l) return;
r=len,len++;
sort(to+l,to+1+r);
if (s[u]==0) s[u]=1;
for (s[u]=l;s[u]<=r;++s[u]){
if (s[u]==r){
if (flg[to[s[u]]]&&flg[u]&&flg[fa]&&to[s[u]]>last&&cir<0){
cir=1;
return;
}
dfs(to[s[u]],last,u);
}
else dfs(to[s[u]],to[s[u]+1],u);
}
}
int main(){
n=gi(),m=gi();
for (int i=1;i<=m;i++){
int u=gi(),v=gi();
G[u].pb(v); G[v].pb(u);
de[u]++; de[v]++;
}
for (int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
if (!(n^m)) { cir=-1; cnt=n; solve(); flg[0]=true;}
dfs(1,n+1,0);
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
[NOIP提高组2018day2t1]旅行的更多相关文章
- NOIP提高组2004 合并果子题解
NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消 ...
- 计蒜客 NOIP 提高组模拟竞赛第一试 补记
计蒜客 NOIP 提高组模拟竞赛第一试 补记 A. 广场车神 题目大意: 一个\(n\times m(n,m\le2000)\)的网格,初始时位于左下角的\((1,1)\)处,终点在右上角的\((n, ...
- 1043 方格取数 2000 noip 提高组
1043 方格取数 2000 noip 提高组 题目描述 Description 设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0.如下图所示(见样 ...
- [NOIP提高组2018]货币系统
[TOC] 题目名称:货币系统 来源:2018年NOIP提高组 链接 博客链接 CSDN 洛谷博客 洛谷题解 题目链接 LibreOJ(2951) 洛谷(P5020) 大视野在线评测(1425) 题目 ...
- NOIP提高组初赛难题总结
NOIP提高组初赛难题总结 注:笔者开始写本文章时noip初赛新题型还未公布,故会含有一些比较老的内容,敬请谅解. 约定: 若无特殊说明,本文中未知数均为整数 [表达式] 表示:在表达式成立时它的值为 ...
- 津津的储蓄计划 NOIp提高组2004
这个题目当年困扰了我许久,现在来反思一下 本文为博客园ShyButHandsome的原创作品,转载请注明出处 右边有目录,方便快速浏览 题目描述 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津\ ...
- 2018.12.30【NOIP提高组】模拟赛C组总结
2018.12.30[NOIP提高组]模拟赛C组总结 今天成功回归开始做比赛 感觉十分良(zhōng)好(chà). 统计数字(count.pas/c/cpp) 字符串的展开(expand.pas/c ...
- 2018.12.08【NOIP提高组】模拟B组总结(未完成)
2018.12.08[NOIP提高组]模拟B组总结 diyiti 保留道路 进化序列 B diyiti Description 给定n 根直的木棍,要从中选出6 根木棍,满足:能用这6 根木棍拼出一个 ...
- 【NOIP2015模拟11.5】JZOJ8月5日提高组T3 旅行
[NOIP2015模拟11.5]JZOJ8月5日提高组T3 旅行 题目 若不存在第\(k\)短路径时,输出"Stupid Mike" 题解 题意 给出一个有\(n\)个点的树 问这 ...
随机推荐
- A2dp初始化流程源码分析
蓝牙启动的时候,会涉及到各个profile 的启动.这篇文章分析一下,蓝牙中a2dp profile的初始化流程. 我们从AdapterState.java中对于USER_TURN_ON 消息的处理说 ...
- python中使用pymongo操作mongo
MongoDB是由C++语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似JSON对象,它的字段值可以包含其他文档.数组及文档数组,非常灵活.在这一节中,我们就来看 ...
- (7)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 利用Polly+AOP+依赖注入封装的降级框架
创建简单的熔断降级框架 要达到的目标是: 参与降级的方法参数要一样,当HelloAsync执行出错的时候执行HelloFallBackAsync方法. public class Person { [H ...
- Mysql8.0的登录大坑……(忘记登录密码也可以这么搞)
关于安装和使用就不说了,属于基本操作了: 我来重点记录一下关于使用前,使用navicat登录的时候报错,1130和2059 查看安装后随机生成的密码: grep 'temporary password ...
- 理解Liang-Barsky裁剪算法的算法原理
0.补充知识向量点积:结果等于0, 两向量垂直; 结果大于0, 两向量夹角小于90度; 结果小于0, 两向量夹角大于90度.直线的参数方程:(x1, y1)和(x2, y2)两点确定的直线, 其参数方 ...
- Centos下堡垒机Jumpserver V3.0环境部署完整记录(2)-配置篇
前面已经介绍了Jumpserver V3.0的安装,基于这篇安装文档,下面说下Jumpserver安装后的的功能使用: 一.jumpserver的启动 Jumpserver的启动和重启 [root@t ...
- sigar开发(java)
下载sigar,地址:https://yunpan.cn/cBEWbEfdAm98f (提取码:f765) 可以收集的信息 CPU信息:包括基本信息(vendor.model.mhz.cacheSiz ...
- 1023 C. Bracket Subsequence
传送门 [http://codeforces.com/contest/1023/problem/C] 题意 n字符串长度,k要求的字符串的长度,字符串只包含'('和')',而且这两种的数量相等,要求的 ...
- final个人阅读作业
一.软件工程M1/M2总结 1.M1阶段总结: 我们团队的软件工程开发是按照前后端来分别开发的,我是负责后端的.我们的项目是做一个北航的社团平台,是一个网站.在后端我们使用的是ruby on rail ...
- Linux内核分析——字符集总结与分析
一. 设置修改系统.应用默认字符集 1. 查看虚拟机的字符集: 由此可见,该虚拟机的字符集为zh_CN.UTF-8. 2. 查看服务器支持的编码方式 3. 修改字符集类型 上图可见,LANG字符 ...