题解:[GXOI/GZOI2019]旅行者

调这个题调了两个月,被自己蠢哭

题意:

   给一个有向图,一组关键点,求关键点之间的最短的距离

Solution:

  这个题目有两种做法,分别是 $nlogn$ 和 $nlog^2n$ 的

  首先说 $nlogn$ 的官方做法,我们考虑多源迪杰斯特拉

正图上从 k 个关键点出发跑 $dijkstra$ ,记某个点离最近的关键点距离为 $dis[0][i]$

反图上也从 k 个关键点出发跑 $dijkstra$ ,距离记为 $dis[1][i]$

枚举正图中的边 $u->v: w$, 用 $dis[0][u]+dis[1][v]+w$ 更新答案

  然后就是一种很好打的 $nlognlogk$ 的做法,我们考虑一种思想,二进制分组

对于每一个在集合中的元素,我们进行重新标号,然后对每一位进行$0/1$二进制分组,由于每个元素的编号不一样,所以至少有一位的分组不同,然后一组连 $S$,一组连 $T$,这样跑 $logk$ 组的从 $S$ 到 $T$ 的最短路,去 $min$,即可

#include <bits/stdc++.h>
using namespace std;

#define re register
#define ll long long
#define gc getchar()
inline int read()
{
 	re int x(0),f(1);re char c(gc);
    while(c>‘9‘||c<‘0‘)f=c==‘-‘?-1:1,c=gc;
    while(c>=‘0‘&&c<=‘9‘)x=x*10+c-48,c=gc;
    return f*x;
}

const int N=5e5+10,M=1e6+10;
ll INF=1e15+7;
int h[N],n,m,cnt,k,a[N],x[N],y[N],z[N],s,t;
struct node {int next,to,w;}e[M<<1];
ll dis[N],ans=INF;
void add(int u,int v,int w){e[++cnt]=(node){h[u],v,w},h[u]=cnt;}
#define QXX(u) for(int i=h[u],v;v=e[i].to,i;i=e[i].next) 

struct Node
{
    int id;ll d;
    bool operator < (const Node a) const {return d>a.d;}
};
priority_queue<Node>q;

void dijkstra()
{
    while(!q.empty()) q.pop();
    for(int i=1;i<=t;++i)
        dis[i]=INF;
    dis[s]=0;
    Node a;
    int u,d;
    q.push((Node){s,0});
    while(!q.empty())
    {
        a=q.top(),q.pop();
        u=a.id,d=a.d;
        if(d!=dis[u]) continue;
        QXX(u)
            if(dis[u]+e[i].w<dis[v])
            {
                dis[v]=dis[u]+e[i].w;
                q.push((Node){v,dis[v]});
            }
    }
} 

void work()
{
    ans=INF;
    n=read(),m=read(),k=read();
    s=n+1,t=n+2;
    for(int i=1;i<=m;++i)
        x[i]=read(),y[i]=read(),z[i]=read();
    for(int i=1;i<=k;++i)
        a[i]=read();
    for(int q=0;(1<<q)<=k;++q)
    {
        memset(h,0,sizeof(h));
        cnt=0;
        for(int i=1;i<=k;++i)
        {
            if(i&(1<<q)) add(s,a[i],0);
            else add(a[i],t,0);
        }
        for(int i=1;i<=m;++i)
            add(x[i],y[i],z[i]);
        dijkstra();
        ans=min(ans,dis[t]);
        memset(h,0,sizeof(h));
        cnt=0;
        for(int i=1;i<=k;++i)
        {
            if(i&(1<<q)) add(s,a[i],0);
            else add(a[i],t,0);
        }
        for(int i=1;i<=m;++i)
            add(y[i],x[i],z[i]);
        dijkstra();
        ans=min(ans,dis[t]);
    }
    cout<<ans<<endl;
}

int main()
{
    int T=read();
    while(T--) work();
    return 0;
}

  

原文地址:https://www.cnblogs.com/zijinjun/p/11137356.html

时间: 2024-10-07 02:28:38

题解:[GXOI/GZOI2019]旅行者的相关文章

题解-GXOI/GZOI2019 特技飞行

Problem loj3085 bzoj不放题面差评 题意概要:给出两条竖直直线,再给出 \(n\) 架飞机的初始航线:一条接通这两条直线的线段,保证航线交点不在两条直线上.现要求安排所有飞机在航线相交处做特技: 擦身而过:两架飞机按原方向线路继续前进,一次得分 \(b\) 对向交换:两架飞机交换线路继续前进,一次得分 \(a\) 另外,给定 \(k\) 个边界与坐标轴成 \(45°\)角 的正方形,若一次特技被至少一个正方形囊括,则总得分加 \(c\) 现要求决策每次相遇做的特技,求最大/最小

[GXOI/GZOI2019]旅行者

就我感觉这道题很神仙吗/kel 仔细想想应该也是一种适用范围挺广的做法. 考虑我们可以通过dijkstra在O(nlogn)求出一个点集到另外一个点集的最短路. 那么我们可以通过一些划分点集的方式使得每一对点都被计算一次. 考虑按照二进制划分. 两个不同的数至少有一个二进制位不同. 按照每一个二进制位01分组,跑logn次dijkstra即可得出答案. #include<bits/stdc++.h> #define N 220000 #define M 1100000 #define eps

Luogu P5304 [GXOI/GZOI2019]旅行者

有趣的题目,又好想又好码,可谓是省选题中的一股清流 考虑如果我们枚举一个点作为起点,然后暴力求出到其它点的最短路,那么可以暴力解决问题 但是我们稍微转化一下问题,同时把一些点的集合作为起点,跑出到其它剩下所有点的最短路,取出其中最小的一条,就相当于同时做了多次猜测 具体实现也非常简单,直接建个超级起点\(st\)和终点\(tar\),如果这个关键点\(x\)位于起点集合那么连一条\(st\to x,val=0\)的边,在终点集合就连一条\(x\to tar,val=0\)的边 最后\(st\to

[GXOI/GZOI2019旅行者]

#include <queue> #include <cstdio> #include <cctype> #include <cstring> #include <iostream> #include <algorithm> typedef long long LL; using namespace std; const int maxn = 1e5+6,inf = 1e9; int T,n,m,k; int city[maxn],f

Loj #3085. 「GXOI / GZOI2019」特技飞行

Loj #3085. 「GXOI / GZOI2019」特技飞行 题目描述 公元 \(9012\) 年,Z 市的航空基地计划举行一场特技飞行表演.表演的场地可以看作一个二维平面直角坐标系,其中横坐标代表着水平位置,纵坐标代表着飞行高度. 在最初的计划中,这 \(n\) 架飞机首先会飞行到起点 \(x = x_{st}\) 处,其中第 \(i\) 架飞机在起点处的高度为 \(y_{i,0}\).它们的目标是终点 \(x = x_{ed}\) 处,其中第 \(i\) 架飞机在终点处的高度应为 \(y

[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)

[GXOI/GZOI2019]旧词 (树链剖分) 题面 给定一棵 \(n\)个点的有根树,节点标号 \([1,n]\),1号节点为根. 给定常数\(k\) 给定\(Q\)个询问,每次询问给定\(x,y\),求:\(\sum_{i=1}^x \mathrm{deep}(\mathrm{lca}(i,y)) \mod 998244353\) 分析 此题为[BZOJ3626] [LNOI2014]LCA(树链剖分)的加强版. 考虑原来的做法(k=1):我们把i到根的路径上所有点+1,y到根路径上的权值

题解 P5301 【[GXOI/GZOI2019]宝牌一大堆】

这道题除了非常恶心以外也没有什么非常让人恶心的地方 当然一定要说有的话还是有的,就是这题和咱 ZJOI 的 mahjong 真的是好像的说~ 于是就想说这道题出题人应该被 锕 掉 noteskey 整体的思路就是特判国士无双和七对子,然后 dp 搞普通的胡牌 dp 状态设计和楼上大佬说的一样,就是用一个五维的 \(f[i][j][k][l][p]\) 表示当前处理了前 i 种类型的牌,存在 j 个 面子/杠子 ,以 i-1 开头的顺子要选 k 个,以 i 开头的面子要选 l 个,以及当前是否有

[GXOI/GZOI2019]与或和

/* 显然位之间会互不影响, 然后分位来统计, 显然&只有全1才有贡献, 显然|只有全0才没贡献 分别n^2处理即可 */ #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #define ll long long #define M 1010 using namespace std; int read() {

[GX/GZOI2019]旅行者(dijkstra)

二进制分组SB做法没意思还难写还可能会被卡常其实是我不会写.用一种比较优秀的O(Tnlogn)做法,只需要做2次dijkstra.对原图做一次.对反图做一次,然后记录每个点的最短路是从k个源点中的哪个转移过来的.然后枚举每条边,若两边转移过来的源点不同,则用d1+w[i]+d2来更新答案即可. #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+7,M=5e5+7; struct