模拟赛题讲解[25]
来自 s_r_f 2023-08-02 noi.ac #3193
题目描述:
小 C 生活在一个城市里。这个城市里有 $n$ 个点 $1,2,\cdots n$ 和 $k$ 条地铁线路,每条地铁线路用 $(l,a,b,c)$ 表示,包含 $(l,l+a,l+2a,l+3a…,l+ab)$ ,保证 $1 \leq l < l + ab \leq n$,$a,b \geq 1,c \geq 0$. 可以从地铁线路中的任何一站以 $c$ 的费用到达线路中的任何其它站。
还有 $n-1$ 条道路,连接点 $i$ 和 $i+1$,费用为 $v_i$。所有的道路都是双向的。
因为城市建设的某些原因,不存在两个 $a$ 相等的地铁线路。
小 C 想知道,从 $1$ 出发,只通过给出的道路和线路,到 $2,3,\cdots,n$ 的最少费用。
输入格式:
第一行,两个整数 $n$ 和 $k$ ,用空格隔开。
接下来 $k$ 行,每行四个整数 $l,a,b,c$,用空格隔开。
接下来 $1$ 行 $n-1$ 个整数 $v_1 \cdots v_{n-1}$ ,用空格隔开。
输出格式:
输出一行。
一行 $n-1$ 个数 $\mathrm{ans}_2,~\mathrm{ans}_3,~\cdots,~\mathrm{ans}_n$ ,用空格隔开。
样例输入1:
3 1
1 2 1 5
100 5
样例输出1:
10 5
数据范围:
子任务 1(30pts) : $n,k \leq 1000$
子任务 2(30pts) $:$ $c,v_i = 1$
子任务 3(30pts) $:$ $n \leq 20000$
子任务 4(10pts) : $2 \leq n \leq 50000,k \leq n ,0 \leq c,v_i \leq 10^9$
题解:
考虑将进入地铁看作一个点 $P$ ,对于当前地铁点集中的所有点 $x$ ,连边 $(x,P,c)$ 和 $(P,x,0)$ 。
分析下复杂度。注意到 $a$ 两两不同,且每组边最多涉及 $\mathcal{O}(n/a)$ 个点
观察到 $\sum_{i=1}^n\left\lceil\frac{n}{i}\right\rceil$ 在 $n=50000$ 时是 $T=598695$ ,复杂度不超过 $\Theta(T \log T)$ 。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(5e4 + 15))
int n,m,pos=1,head[N << 2],d[N << 2]; bitset<(N << 2)> vis;
struct Edge { int u,v,w,next; } e[N << 6];
void addEdge(int u,int v,int w) { e[++pos] = {u,v,w,head[u]}; head[u] = pos++; }
struct node { int u,dis; };
priority_queue<node> q;
bool operator<(node a,node b) { return a.dis > b.dis;}
void Dijkstra()
{
memset(d, 0x3f, sizeof(d));
d[1] = 0; q.push({1, 0});
while(!q.empty())
{
int u = q.top().u; q.pop(); if(vis[u]) continue; vis[u] = 1;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if(d[v] > d[u] + e[i].w)
{
d[v] = d[u] + e[i].w;
q.push({v, d[v]});
}
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m;
for(int i = 1, l,a,b,c; i <= m; i++)
{
cin >> l >> a >> b >> c;
for(int j = l; j <= n && j <= l + a * b; j += a) {
addEdge(j, n + i, 0); addEdge(n + i, j, c);
}
}
for(int i = 1, w; i < n; i++) {
cin >> w; addEdge(i, i + 1, w); addEdge(i + 1, i, w);
}
Dijkstra();
for(int i = 2; i <= n; i++) cout << d[i] << " \n"[i == n];
return 0;
}