边的处理
题目背景:
11.03 NOIP模拟T3
分析:分治
感觉很妙妙的一道题,首先我们发现,因为每一条边只要在询问范围内,就一定要被使用,那么我们就可以发现,这是可以合并的,比如说一种从x经过i ~ j的所有边然后到达y的方案,和一个从y出发经过j + 1 ~ k的所有边最后到达z的方案可以合并成为一个从x出发经过i ~ k的所有边最后到达z的一个方案,那么,我们可以选择尽可能多的区间来进行合并会显得非常稳,那么考虑如何有效的分割区间,这个时候我们就可以想到分治了,我们先把询问离线,然后用对应的边集进行处理,假设我们现在的边集是l ~ r,mid = (l + r) / 2,那么我们可以通过处理从l ~ mid当中任意一条边开始一直走到第mid条边的方案数,和从mid + 1开始走到mid + 1 ~ r当中的任意一条边的方案数,然后就可以处理出所有询问的边集跨过了中间第mid条边的询问,直接通过枚举中间点,来合并两边的方案,找到最优的方案就可以了。具体我们定义left[i][j][k]表示,走完第i条边 ~ 第mid条边,从k à j的最小代价,right[i][j][k]表示,走完第mid + 1条边到第i条边,从j à k的最小代价,然后对于询问直接可以枚举中间点j,考虑如何得到right和left,我们只需要转移的时候考虑是走过第i条边,还是不走第i条边,还是没有办法到达当前状态即可,复杂度应该是O(qn + qlogm + mlogm * n2),具体详见代码
Source:
/*
created by scarlyw
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <ctime>
const int MAXN = 30 + 5;
const int MAXM = 20000 + 10;
const int MAXQ = 200000 + 10;
const int INF = 1000000000;
struct query {
int u, v, l, r, id;
} q[MAXQ], temp[MAXQ];
struct edge {
int x, y, r, c;
} e[MAXM];
int n, m, t;
int ans[MAXQ];
int left[MAXM][MAXN][MAXN], right[MAXM][MAXN][MAXN];
inline void solve(int l, int r, int ql, int qr) {
if (ql > qr) return ;
if (l == r) {
for (int i = ql; i <= qr; ++i) {
ans[q[i].id] = INF;
if ((q[i].u == e[l].x && q[i].v == e[l].y) ||
q[i].u == e[l].y && q[i].v == e[l].x)
ans[q[i].id] = e[l].c;
if (q[i].u == q[i].v) ans[q[i].id] = std::min(ans[q[i].id], e[l].r);
(ans[q[i].id] == INF) ? (ans[q[i].id] = -1) : 0;
}
return ;
}
int mid = l + r >> 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
left[mid + 1][i][j] = right[mid][i][j] = INF;
for (int i = 1; i <= n; ++i)
left[mid + 1][i][i] = right[mid][i][i] = 0;
for (int i = mid; i >= l; --i) {
for (int u = 1; u <= n; ++u)
for (int v = 1; v <= n; ++v)
left[i][u][v] = left[i + 1][u][v] + e[i].r;
for (int u = 1; u <= n; ++u) {
if (left[i + 1][u][e[i].x] < INF)
left[i][u][e[i].y] = std::min(left[i][u][e[i].y],
left[i + 1][u][e[i].x] + e[i].c);
if (left[i + 1][u][e[i].y] < INF)
left[i][u][e[i].x] = std::min(left[i][u][e[i].x],
left[i + 1][u][e[i].y] + e[i].c);
}
}
for (int i = mid + 1; i <= r; ++i) {
for (int u = 1; u <= n; ++u)
for (int v = 1; v <= n; ++v)
right[i][u][v] = right[i - 1][u][v] + e[i].r;
for (int u = 1; u <= n; ++u) {
if (right[i - 1][u][e[i].x] < INF)
right[i][u][e[i].y] = std::min(right[i][u][e[i].y],
right[i - 1][u][e[i].x] + e[i].c);
if (right[i - 1][u][e[i].y] < INF)
right[i][u][e[i].x] = std::min(right[i][u][e[i].x],
right[i - 1][u][e[i].y] + e[i].c);
}
}
for (int i = ql; i <= qr; ++i) {
if (q[i].l <= mid && q[i].r > mid) {
int val = INF;
for (int k = 1; k <= n; ++k)
val = std::min(val, left[q[i].l][k][q[i].u] +
right[q[i].r][k][q[i].v]);
ans[q[i].id] = (val == INF) ? -1 : val;
}
}
int new_l = ql - 1, new_r = qr + 1;
for (int i = ql; i <= qr; ++i) temp[i] = q[i];
for (int i = ql; i <= qr; ++i) if (temp[i].r <= mid) q[++new_l] = temp[i];
for (int i = qr; i >= ql; --i) if (temp[i].l > mid) q[--new_r] = temp[i];
solve(l, mid, ql, new_l), solve(mid + 1, r, new_r, qr);
}
inline void read_in() {
scanf("%d%d%d", &n, &m, &t);
for (int i = 1; i <= m; ++i)
scanf("%d%d%d%d", &e[i].x, &e[i].y, &e[i].c, &e[i].r);
for (int i = 1; i <= t; ++i)
scanf("%d%d%d%d", &q[i].u, &q[i].v, &q[i].l, &q[i].r), q[i].id = i;
}
inline void solve() {
solve(1, m, 1, t);
for (int i = 1; i <= t; ++i) std::cout << ans[i] << "\n";
}
int main() {
read_in();
solve();
return 0;
}