[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\)个点的树 问这 ...
随机推荐
- Jenkins构建自动化任务
前言 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. 一.环境配置 1.切换到jenkins ...
- JS进阶系列之执行上下文
function test(){ console.log(a);//undefined; var a = 1; } test(); 也许你会遇到过上面这样的面试题,你只知道它考的是变量提升,但是具体的 ...
- 1023 C. Bracket Subsequence
传送门 [http://codeforces.com/contest/1023/problem/C] 题意 n字符串长度,k要求的字符串的长度,字符串只包含'('和')',而且这两种的数量相等,要求的 ...
- Flask、Celery、RabbitMQ学习计划
Flask (9.16-9.23) 相关组件了解 (9.16-17) WSGI:Werkzeug 数据库:SQLAlchemy *重点查看 urls和视图 (9.18-19) session和co ...
- 补充照片:某基同学使用Bing词典
某基同学使用Bing词典的照片
- <<梦断代码>>阅读笔记二
这是第二篇读书笔记,这本书我已经读了有一大半了,感觉书中所描述的人都是疯子,一群有创造力,却又耐得住寂寞的疯子. 我从书中发现几点我比较感兴趣的内容. 第一个,乐高之梦.将程序用乐高积木一样拼接起来. ...
- C++课程学习建议
从C到C++,学院都采用了机房授课模式,也在探索更为高效的实践与理论融合的教学方法,对于课程学习来说,仍有以下建议: 1.多看书.看书是理解基本概念的必备手段.也是学习的根本.应将课前预习.课后复习联 ...
- 作业1+2.四则运算(改进后完整版,用python写的)_064121陶源
概述: 用一个星期加上五一的三天假期自学了python,在Mac系统上重新写出了四则运算的程序,编译器是PyCharm,相当于完成了作业2.d)"选一个你从来没有学过的编程语言,试一试实现基 ...
- Geekers团队成立日志
大家好,作为团队的队长,今天在这里非常荣幸能够发表我们团队的第一篇博客,来宣布我们团队的名字:Geekers! Geek,英文中代表“怪人”,随着时代进步Geek被赋予了新的含义——极客!Steve ...
- 3-palindrome CodeForces - 805B (思维)
In the beginning of the new year Keivan decided to reverse his name. He doesn't like palindromes, so ...