[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\)个点的树 问这 ...
随机推荐
- WPF中TreeView.BringIntoView方法的替代方案
原文:WPF中TreeView.BringIntoView方法的替代方案 WPF中TreeView.BringIntoView方法的替代方案 周银辉 WPF中TreeView.BringIntoVie ...
- eclipse 打包
add directory entries 不勾选, spring 自动扫描之类的扫描不到
- [Oracle]跨DBLINK的JOIN查询的数据库缓存问题15783452141
客户问到跨DBLINK,结合本地表和远端表的时候,数据在哪一边 的 Data Buffer 缓存. 测试的结果是:本地表在本地缓存,远端表在远端缓存. ####Testcase-0929-10 本地数 ...
- openhtmltopdf 支持自定义字体、粗体
一.支持自定义字体 private static void renderPDF(String html, OutputStream outputStream) throws Exception { t ...
- “论 ofo 是如何影响今日头条发展的”
近段时间, ofo 小黄车押金难退的消息频频曝出.尽管 OFO 已经宣布押金只能在线上退还,但是线上退押金也难,因此很多的用户还是选择到 ofo 北京总部“要个说法”.记者昨天在现场发现,位于北京中关 ...
- element-ui + vue + node.js 与 服务器 Python 应用的跨域问题
跨越问题解决的两种办法: 1. 在 config => index.js 中配置 proxyTable 代理: proxyTable: { '/charts': { target: 'http: ...
- jenkins中配置svn 出现absolute path is not allowed
代码: 兵马未动,粮草先行 作者: 传说中的汽水枪 如有错误,请留言指正,欢迎一起探讨. 转载请注明出处. 想用jenkins作自动化部署tomcat. svn代码已经checkout到本地目录了(/ ...
- vue 动态修改 css
<div v-for="i in resultDate" v-if="i.ProjectId>='4'" @click=EveyTesttInfo( ...
- Hitchhiker 是一款开源的 Restful Api 测试工具
Hitchhiker 是一款开源的 Restful Api 测试工具 开源API测试工具 Hitchhiker v0.4更新 - 没有做不到,只有想不到 Hitchhiker 是一款开源的 Restf ...
- Maven的课堂笔记2
5 maven的核心概念 5.1 项目对象模型 说明: maven根据pom.xml文件,把它转化成项目对象模型(POM),这个时候要解析依赖关系,然后去相对应的maven库中查找到依赖的jar包. ...