香港记者

Description

众所周知,香港记者跑得比谁都快,这其中其实是有秘诀的。
首先他们会跑最短路,但是最短路会有很多条,而且其他记者也知道要跑最短路,香港记者还统计出了每座城市带黑框眼镜的人数,如果一个记者跑路的时候城市带黑框眼镜人数的序列字典序比另一个记者大,那么这个记者就会被不可描述的力量续走时间,导致他跑得没字典序小的记者快。
长者日续万秒日理万机,想请你告诉他香港记者经过的总路程和城市带黑框眼镜人数的序列,方便他找到香港记者,传授他们一些人生经验。
方便起见,设起点为 1 终点为 n。
由于续命的多样性不可描述的力量,各个城市带黑框眼镜的人数各不相同。

Input

输入文件名为 journalist.in。
第一行,两个整数 n; m 表示有 n 个城市,城市之间有 m 条有向边。
第二行, n 个数,表示每个城市带黑框眼镜的人数 bi:
接下来 m 行,每行 3 个非负整数 ui; vi; wi 表示一条有向边的起点,终点,路程。

Output

输出文件名为 journalist.out。
第一行,一个非负整数表示香港记者经过的总路程。
第二行,若干个非负整数表示香港记者经过的城市带黑框眼镜人数的序列。

Sample Input

8 9
1 2 3 4 5 6 7 8
1 2 2
2 3 3
3 8 3
1 4 3
4 5 2
5 8 1
1 6 1
6 7 2
7 8 3

Sample Output

6
1 4 5 8

Hint

对于前 30% 的数据, 2 ≤ n ≤ 2 × 103, 1 ≤ m ≤ 4 × 103。
对于前 60% 的数据,保证数据随机。
对于另外 30% 的数据,保证所有起点到终点的简单路径(没有环的路径)长度相同。
对于 100% 的数据, 2 ≤ n ≤ 2 × 105, 1 ≤ m ≤ 4 × 105, 1 ≤ w ≤ 1 × 109,存在至少一条从
起点到终点的最短路。

题解

解法一:

我们可以先求出最短路。

然后搜索求出路径。

对于u−>v,如果

dist[v]==dist[u]+w(u,v)

就继续拓展。

我们可以加边的时候先将后继节点按点权排序,这样贪心保证搜出的第一条策略就是满足条件的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lol;
template <typename T> void read(T &x)
{
    x=0;char c=getchar();
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())x=x*10+c-‘0‘;
}
int n,m,s[200005];
struct Edge
{
    int from,to;lol dis;
}map[400005];
int size=0,head[200005];
struct node
{
    int next,to;
    lol dis;
}edge[400005];
void putin(int from,int to,lol dis)
{
    size++;
    edge[size].next=head[from];
    edge[size].to=to;
    edge[size].dis=dis;
    head[from]=size;
}
bool cmp(const Edge a,const Edge b)
{
    if(a.from!=b.from)return a.from<b.from;
    if(a.to!=b.to)return s[a.to]>s[b.to];
    else return a.dis<b.dis;
}
lol dist[200005];
int vis[200005],pre[200005];
void SPFA(int r)
{
    memset(dist,127/3,sizeof(dist));
    queue<int>mem;
    mem.push(r);
    vis[r]=1;
    dist[r]=0;
    while(!mem.empty())
    {
        int x=mem.front();mem.pop();
        vis[x]=0;
        for(int i=head[x];i!=-1;i=edge[i].next)
        {
            int y=edge[i].to;
            if(dist[y]>dist[x]+edge[i].dis)
            {
                dist[y]=dist[x]+edge[i].dis;
                if(!vis[y])
                {
                    mem.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    return;
}
bool ok;
void dfs(int r)
{
    if(r==n)ok=1;
    if(ok)return;
    int i;
    for(i=head[r];i!=-1;i=edge[i].next)
    {
        int y=edge[i].to;
        if(dist[y]==dist[r]+edge[i].dis)
        {
            pre[y]=r;
            dfs(y);
        }
    }
}
int ans[200005],cnt;
void write(int r)
{
    while(r!=1)
    {
        ans[++cnt]=s[r];
        r=pre[r];
    }
    ans[++cnt]=s[r];
    for(int i=cnt;i>=2;i--)printf("%d ",ans[i]);
    printf("%d",ans[1]);
    return;
}
int main()
{
    freopen("journalist.in","r",stdin);
    freopen("journalist.out","w",stdout);
    int i,j;
    read(n);read(m);
    memset(head,-1,sizeof(head));
    for(i=1;i<=n;i++)read(s[i]);
    for(i=1;i<=m;i++)
    {
        read(map[i].from);
        read(map[i].to);
        read(map[i].dis);
    }
    sort(map+1,map+m+1,cmp);
    for(i=1;i<=m;i++)putin(map[i].from,map[i].to,map[i].dis);
    SPFA(1);
    printf("%lld\n",dist[n]);
    dfs(1);
    write(n);
    return 0;
}

解法二:

我们可以存储逆边,逆向做一次SPFA。

pre[u]表示u的后继节点

松弛的时候如果

dist[v]>dist[u]+w(u,v)

dist[v]=dist[u]+w(u,v);

如果

dist[v]==dist[u]+w(u,v)

此时若pre[v]的点权大于u的点权,我们将

pre[v]=u;

由于字典序高位越小越好,满足最优子结构,贪心是可行的。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long lol;
template <typename T> void read(T &x)
{
    x=0;char c=getchar();
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())x=x*10+c-‘0‘;
}
int n,m,size=0,head[200005],s[200005];
struct node
{
    int next,to;
    lol dis;
}edge[400005];
void putin(int from,int to,lol dis)
{
    size++;
    edge[size].next=head[from];
    edge[size].to=to;
    edge[size].dis=dis;
    head[from]=size;
}
lol dist[200005];
int vis[200005],pre[200005];
void SPFA(int r)
{
    memset(dist,127/3,sizeof(dist));
    memset(pre,127/3,sizeof(pre));
    queue<int>mem;
    mem.push(r);
    vis[r]=1;
    dist[r]=0;
    while(!mem.empty())
    {
        int x=mem.front();mem.pop();
        vis[x]=0;
        for(int i=head[x];i!=-1;i=edge[i].next)
        {
            int y=edge[i].to;
            if(dist[y]==dist[x]+edge[i].dis&&s[pre[y]]>s[x])pre[y]=x;
            if(dist[y]>dist[x]+edge[i].dis)
            {
                dist[y]=dist[x]+edge[i].dis;
                pre[y]=x;
                if(!vis[y])
                {
                    mem.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    return;
}
void write(int r)
{
    while(r!=n)
    {
        printf("%d ",s[r]);
        r=pre[r];
    }
    printf("%d",s[r]);
    return;
}
int main()
{
    freopen("journalist.in","r",stdin);
    freopen("journalist.out","w",stdout);
    int i,j;
    memset(head,-1,sizeof(head));
    read(n);read(m);
    for(i=1;i<=n;i++)
    read(s[i]);
    for(i=1;i<=m;i++)
    {
        int from,to,dis;
        read(from);read(to);read(dis);
        putin(to,from,dis);
    }
    SPFA(n);
    printf("%lld\n",dist[1]);
    write(1);
    return 0;
}
时间: 2024-12-28 11:03:17

香港记者的相关文章

[JZOJ5279]香港记者题解--最短路图

[JZOJ5279]香港记者题解--最短路图 题目链接 过 于 暴 力 分析 有一个naiive的想法就是从1到n跑最短路,中途建图,然后在图上按字典序最小走一遍,然而·这是不行的,你这样跳不一定能跳到终点. 所以应该是在1到n的最短路图上跳,怎么求有向图短路图?你跑一遍1到n得到\(dist1[]\),n到1的最短路得到\(dist[2]\),然后从1 BFS,对于原图一条\(u\),连向\(v\)的边,若\(dis1[u]+dis2[v]+dis(u,v)\)等于1到n的最短路距离,则最短路

XX-net的正确食用姿势

[序] 中国的政府有一个好,全世界哪个网站,比西方政府墙的还快,但墙来墙去的办法都是too...too..sometimes.... 于是我们知道有一堵互联网墙挡在天朝与其他国家之间. 然而这并不是很多OIer所希望看到的.因为歪果有些个GCJ,TC,CF,比BC不知道高到哪里去了,OIer们要跟它们谈笑风生. 这是我们就需要一些螳臂当车的歹徒,来带领我们翻越这堵墙.本次介绍歹徒之一——XX-net. 两三年前,有个叫GoAgent的项目,通过申请GAE AppID来利用谷歌服务器FQ,这就是X

天才辈出的数学江湖

这篇日志的原因很简单,就是希望给所有期望在学科研究上能有所建树的学子们一面镜子,镜子里不仅有这位昔日的IMO金牌.北大数院毕业后前往MIT攻读博士后.29岁拿到拉马努金奖.现在Stanford做tenure的大神恽之玮,还有比利时数学家Pierre Deligne(皮埃尔·德利涅)以及德国数学家Gerd Faltings (格尔德·法尔廷斯)和Peter Scholze等大神的影子,更为重要的是,作者揭示了大神之所以能称为"大神",背后所付出的一切,同时又用一个个鲜活的事例演示了现代版

YYHS-怎样更有力气

题目描述 OI大师抖儿在夺得银牌之后,顺利保送pku.这一天,抖儿问长者:"我虽然已经保送了,但我的志向是为国家健康工作五十年.请问我应该怎样变得更有力气?"  长者回答:"你啊,Too Young Too Simple,Sometimes Naive!如果你想要我教你,你要先进行艰苦的修行." 长者的住宅中有一堵长度为n的墙.每天抖儿起床修行,会选择一段长度为x的区间染成白色.长者的住宅附近有一群香港记者,为了借助抖儿拜访长者,第i天香港记者会将区间[li,ri]

[测试题]钦点

钦点 问题描述之所以续走的那些香港记者,是因为他们掌握了长者钦点董先生的关键证据,现在这份证据落到了你的手里.这份文件是一个 n × m 的矩形,矩形内每一个元素是一个字符串.这份文件经过了 q 次加密,每次加密是交换两个长宽分别相等的矩形,由于长者有点老花眼,所以他加密的时候两个矩形间任意一对元素曼哈顿距离大于 1.由于好奇,你开始解密这份文件,方便起见,加密操作已经倒序给你,你只要按顺序操作一遍便能解密.当你破解完这个文件,你会发现你的生命少了做这道题的时间. 输入格式输入文件名为 appo

【模板】tyvjP1520 树的直径 [2017年5月计划 清北学堂Day3]

P1520 树的直径 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 树的直径,即这棵树中距离最远的两个结点的距离.每两个相邻的结点的距离为1,即父亲结点与儿子结点或儿子结点与父子结点之间的距离为1.有趣的是,从树 的任意一个结点a出发,走到距离最远的结点b,再从结点b出发,能够走的最远距离,就是树的直径.树中相邻两个结点的距离为1.你的任务是:给定一棵树, 求这棵树中距离最远的两个结点的距离. 输入格式 输入共n行第一行是一个正整数n,表示这棵树的结点

数论题总结

这几天做了几道微不足道的数论题,感觉做法都十分的高啊,我这种僵化的思想是很难有那样的切题知识水平的.然后做了几道题,感觉也有点熟悉数学的那一套理论了,虽然我还是太弱,但是有点东西还是要讲出来的嘛,一起谈笑风生,积累人生经验.闷声发大财虽然好,但是是不支持的. 上面那句话看不懂就无视吧. 那么对于数论题,我们应该如何下手呢??我总结了一些分析技巧和优化技巧,希望有用(希望考场推得出来). 题目分析: 1.题目给的很直接了,让你求这个那个. 以两道同年的NOI题目为例. 1.荒岛野人 Savage

一日一测

20170923 liurunda之测 Scanf在考前说,这是一名很关心我们情况的学长,他也很强.嗯,是的. 他在题目中乱模,而且还乱出数据(样例数据如此,测试数据更可恶). 乱模是什么意思?就是说: 3.念诗.大呼”苟利国家生死以,竹外桃花三两枝”,回复b点膜法值.一开始你有c点膜法值.之后你的膜法值可以超过c.香港记者有一个好,脑子转得比谁都快,会选择最优策略.如果你会被续,输出”SIMPLE”,如果香港记者会被续,输出“NAIVE” 额.本人当时自我感觉良好,估分200,结果最后只有30

题解 P3369 【【模板】普通平衡树(Treap/SBT)】

STL真是个好东西. 最近在看pb_ds库及vector和set的用法,就想用这三种操作来实现一下普通平衡树,结果pb_ds中的rbtree不支持重复值,而本蒟蒻也看不懂不懂各大佬用pb_ds的实现,况且应该有人已经贴上了题解.我就发一发vector和set(其实是multiset)的题解吧.(只不过蒟蒻的我我根本不会打splay) 代码都很短,操作其实也很基础. vector版: 你要知道: lower_bound(first,last,x)在first和last中的前闭后开区间进行查找,其中