最短路
P4779 【模板】单源最短路径(标准版)
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int N = 200005;int n, m, s;int h[N], e[N << 1], ne[N << 1], w[N << 1], idx;vector<PII> v[N];int dis[N];struct node{int d, id;bool friend operator<(node a,node b){return a.d > b.d;}};void add(int u,int v,int W){e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;}priority_queue<node> q;void dij(){memset(dis, 0x3f, sizeof(dis));dis[s] = 0;q.push({0, s});while(!q.empty()){int d = q.top().d, u = q.top().id;q.pop();if(d>dis[u])continue;for (int i = h[u]; i;i=ne[i]){int v = e[i];if(dis[v]>dis[u]+w[i]){dis[v] = dis[u] + w[i];q.push({dis[v], v});}}}}void Main(){cin >> n >> m >> s;for (int i = 1; i <= m;i++){int u, v, w;cin >> u >> v >> w;add(u, v, w);}dij();for (int i = 1; i <= n;i++)cout << dis[i] << " ";}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;// T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
P5905 【模板】全源最短路(Johnson)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endlinline int rd() {int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9') {if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}void print(ll x) {if (x < 0)putchar('-'), x = -x;if (x > 9)print(x / 10);putchar(x % 10 + '0');return;
}namespace Star_F {struct edge {int v, w, next;
} e[10005];struct node {int dis, id;bool operator<(const node& a) const { return dis > a.dis; }node(int d, int x) { dis = d, id = x; }
};const int INF = 1e9;
int head[5005], vis[5005], t[5005];
int cnt, n, m;
long long h[5005], dis[5005];void addedge(int u, int v, int w) {e[++cnt].v = v;e[cnt].w = w;e[cnt].next = head[u];head[u] = cnt;
}bool spfa(int s) {queue<int> q;memset(h, 63, sizeof(h));h[s] = 0, vis[s] = 1;q.push(s);while (!q.empty()) {int u = q.front();q.pop();vis[u] = 0;for (int i = head[u]; i; i = e[i].next) {int v = e[i].v;if (h[v] > h[u] + e[i].w) {h[v] = h[u] + e[i].w;if (!vis[v]) {vis[v] = 1;q.push(v);t[v]++;if (t[v] == n + 1) return false;}}}}return true;
}void dijkstra(int s) {priority_queue<node> q;for (int i = 1; i <= n; i++) dis[i] = INF;memset(vis, 0, sizeof(vis));dis[s] = 0;q.push(node(0, s));while (!q.empty()) {int u = q.top().id;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 (dis[v] > dis[u] + e[i].w) {dis[v] = dis[u] + e[i].w;if (!vis[v]) q.push(node(dis[v], v));}}}return;
}void Main() {n = rd(); m = rd();for (int i = 1; i <= m; i++) {int u = rd(), v = rd(), w = rd();addedge(u, v, w);}for (int i = 1; i <= n; i++) addedge(0, i, 0);if (!spfa(0)) {cout << -1 << endl;return;}for (int u = 1; u <= n; u++)for (int i = head[u]; i; i = e[i].next) e[i].w += h[u] - h[e[i].v];for (int i = 1; i <= n; i++) {dijkstra(i);long long ans = 0;for (int j = 1; j <= n; j++) {if (dis[j] == INF)ans += j * INF;elseans += j * (dis[j] + h[j] - h[i]);}cout << ans << endl;}
}}signed main() {// freopen(".in", "r", stdin);// freopen(".out", "w", stdout);return Star_F::Main(), 0;
}
P3385 【模板】负环
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int N = 2005;int dis[N], cnt[N];vector<PII> G[N];bool vis[N];int n, m;bool spfa(){queue<int> q;vis[1] = 1, dis[1] = 0, cnt[1] = 1;q.push(1);while(!q.empty()){int u = q.front();q.pop();vis[u] = 0;for (int i = 0; i < G[u].size();i++){int v = G[u][i].fi, w = G[u][i].se;if(dis[v]>dis[u]+w){dis[v] = dis[u] + w;if(!vis[v]){cnt[v]++;q.push(v);if(cnt[v]>=n)return true;}}}}return false;}void Main(){memset(dis, 0x3f, sizeof(dis));memset(vis, 0, sizeof(vis));memset(cnt, 0, sizeof(cnt));cin >> n >> m;for (int i = 1; i <= n;i++)G[i].clear();for (int i = 1; i <= m;i++){int u, v, w;cin >> u >> v >> w;G[u].push_back({v, w});if(w>=0)G[v].push_back({u, w});}cout << (spfa() ? "YES" : "NO") << endl;}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
P1119 灾后重建
动态加边,动态求全源最短路。
由于输入的时间 \(t\) 按升序排序,不用再排序。直接暴力加边,用 \(\text{Floyd}\) 维护全源最短路即可。
注意编号是 \(0 \sim N-1\) ,所以数组从 \(0\) 开始。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int N = 205;int T, n, m, a[N], f[N][N];void update(int k){for (int i = 0; i < n;i++){for (int j = 0; j < n;j++)f[i][j] = f[j][i] = min(f[i][j], f[i][k] + f[k][j]);}}void Main(){cin >> n >> m;for (int i = 0; i < n;i++)cin >> a[i];memset(f, 0x3f, sizeof(f));for (int i = 0; i <= n;i++)f[i][i] = 0;for (int i = 1; i <= m;i++){int u, v, w;cin >> u >> v >> w;f[u][v] = f[v][u] = w;}cin >> T;int x = 0;while(T--){int u, v, t;cin >> u >> v >> t;while(a[x]<=t&&x<n)update(x++);if(a[u]>t||a[v]>t)cout << -1 << endl;else{if(f[u][v]==0x3f3f3f3f)cout << -1 << endl;elsecout << f[u][v] << endl;}}}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;// T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
P1462 通往奥格瑞玛的道路
看到最大值最小,果断考虑二分。
我们二分一个 \(mid\),每次只经过边权小于 \(mid\) 的边,判断是否能到达终点。
如何判断?和最短路类似,我们用 dis[v]=dis[u]-w[i]
更新 dis 数组即可,所以用大根堆维护。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int N = 10005, M = 100005, INF = 0x3f3f3f3f;int h[N], ne[M], e[M], w[M], idx;int n, m, k;int a[N], l = INF, r, flag;void add(int u,int v,int W){e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;}int dis[N];priority_queue<PII> q;bool check(int mid){memset(dis, -1, sizeof(dis));dis[1] = k;q.push({dis[1], 1});while(!q.empty()){int u = q.top().se, d = q.top().fi;q.pop();if(dis[u]!=d)continue;for (int i = h[u]; i;i=ne[i]){int v = e[i];if(a[v]>mid)continue;if(dis[v]<dis[u]-w[i]&&dis[u]-w[i]>=0){dis[v] = dis[u] - w[i];q.push({dis[v], v});}}}return dis[n] != -1;}void Main(){cin >> n >> m >> k;for (int i = 1; i <= n;i++){cin >> a[i];l = min(l, a[i]), r = max(r, a[i]);}flag = r;for (int i = 1; i <= m;i++){int u, v, w;cin >> u >> v >> w;add(u, v, w), add(v, u, w);}l = a[1];while(l<=r){int mid = l + r >> 1;if(check(mid))r = mid - 1;elsel = mid + 1;}if(l==flag+1)cout << "AFK" << endl;elsecout << l << endl;}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;// T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
UVA11090 Going in Cycle!!
经典套路二分题
二分答案 \(w\),原式 \(\sum c_i\ge w \times *cnt\)。化简得 \(\sum^{cnt}(c_i-w)\ge 0\)。转换为找负环。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int INF = 0x3f3f3f3f;const int N = 305, M = 15000;int m, n;int h[N], e[M], ne[M], idx;double w[M];int tmp = 1;void add(int u,int v,double W){e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;}int nm[N], vis[N];double dis[N];bool spfa(double x){queue<int> q;memset(vis, 1, sizeof(vis));for (int i = 1; i <= n;i++)dis[i] = (double)INF;memset(nm, 0, sizeof(nm));for (int i = 1; i <= m;i++)w[i] -= x;for (int i = 1; i <= n;i++)q.push(i);while(!q.empty()){int now = q.front();q.pop();vis[now] = 0;for (int i = h[now]; i;i=ne[i]){int v=e[i];if(dis[v]>=dis[now]+w[i]){dis[v] = dis[now] + w[i];nm[v] = nm[now] + 1;if(vis[v]==0){q.push(v);}if(nm[v]>=n+1){for (int i = 1; i <= m;i++)w[i] += x;return 1;}}}}for (int i = 1; i <= n;i++) w[i]+=x;return 0;}void Main(){idx = 0;memset(h, 0, sizeof(h));cin >> n >> m;for (int i = 1; i <= m;i++){int u, v, w;cin >> u >> v >> w;add(u, v, (double)w);}double l = 0, r = 100000001;while(r-l>0.0000001){double mid = (l + r) / 2;if(spfa(mid))r = mid;else l = mid;}cout << "Case #" << tmp++ << ": ";if(r==100000001)cout << "No cycle found." << endl;elseprintf("%.2lf\n", l);}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
P2865 [USACO06NOV] Roadblocks G
单源次短路模板题目
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int N=200005;int n, m;int h[N], e[N], ne[N], w[N], idx;int dis[2][N];void add(int u,int v,int W){e[++idx] = v, ne[idx] = h[u], w[idx] = W, h[u] = idx;}struct node{int id, d;bool friend operator<(node a,node b){return a.d > b.d;}};priority_queue<node> q;void dij(){for (int i = 1; i <= n;i++)dis[0][i] = dis[1][i] = 2147483647;dis[0][1] = 0;q.push({1, 0});while(!q.empty()){int u = q.top().id, d = q.top().d;q.pop();if(d>dis[1][u])continue;for (int i = h[u]; i;i=ne[i]){int v = e[i];if(dis[0][v]>d+w[i]){dis[1][v] = dis[0][v];dis[0][v] = d + w[i];q.push({v, dis[0][v]});}if(dis[1][v]>d+w[i]&&dis[0][v]<d+w[i]){dis[1][v] = d + w[i];q.push({v, dis[1][v]});}}}}void Main(){cin >> n >> m;for (int i = 1; i <= m;i++){int u, v, w;cin >> u >> v >> w;add(u, v, w), add(v, u, w);}dij();cout << dis[1][n] << endl;}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;// T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
P1875 佳佳的魔法药水
最短路计数问题。
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << " = " << x << endl
#define ll long long
typedef pair <int, int> PII;
typedef unsigned int uint;
typedef unsigned long long ull;
#define i128 __int128
#define fi first
#define se second
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
#define ClockA clock_t start, end; start = clock()
#define ClockB end = clock(); cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
//#define int long long
inline int rd(){int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}
#define rd rd()void wt(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)wt(x / 10);putchar(x % 10 + '0');return;
}
void wt(char x){putchar(x);
}
void wt(int x, char k){wt(x),putchar(k);
}namespace Star_F{const int N = 3005;int c[N], ans[N], t[N][N];bool f[N];void Main(){int n;cin >> n;for (int i = 1; i <= n;i++)cin >> c[i], ans[i] = 1;int u, v, w;while(scanf("%d%d%d",&u,&v,&w)!=EOF)t[u + 1][v + 1] = t[v + 1][u + 1] = w + 1;for (int i = 1; i < n;i++){int maxn = 0x3f3f3f3f;int b;for (int j = 1; j <= n;j++)if(!f[j]&&c[j]<maxn)b = j, maxn = c[j];f[b] = 1;for (int j = 1; j <= n;j++)if(f[j]&&t[b][j]){if(c[b]+c[j]==c[t[b][j]])ans[t[b][j]] += ans[b] * ans[j];if(c[b]+c[j]<c[t[b][j]])c[t[b][j]] = c[b] + c[j], ans[t[b][j]] = ans[b] * ans[j];}}cout << c[1] << " " << ans[1] << endl;}}signed main(){// freopen(".in","r",stdin);// freopen(".out","w",stdout);ClockA;int T=1;// T=rd;while(T--) Star_F::Main();// ClockB;return 0;
}
P1948 [USACO08JAN] Telephone Lines S
最大值最小,还是考虑二分。
二分 \(mid\) ,把大于 \(mid\) 的边看为 \(1\),小于等于 \(mid\) 得便看为 \(0\),跑最短路即可
当然更优秀的做法是 01BFS
。
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 20010;
int n, m, k;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
deque<int> q;
bool st[N];
void add(int a, int b, int c){e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool check(int bound){memset(dist, 0x3f, sizeof dist);memset(st, 0, sizeof st);q.push_back(1);dist[1] = 0;while (q.size()){int t = q.front();q.pop_front();if (st[t]) continue;st[t] = true;for (int i = h[t]; ~i; i = ne[i]){int j = e[i], x = w[i] > bound;if (dist[j] > dist[t] + x){dist[j] = dist[t] + x;if (!x) q.push_front(j);else q.push_back(j);}}}return dist[n] <= k;
}
int main(){cin >> n >> m >> k;memset(h, -1, sizeof h);while (m -- ){int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);}int l = 0, r = 1e6 + 1;while (l < r){int mid = l + r >> 1;if (check(mid)) r = mid;else l = mid + 1;}if (r == 1e6 + 1) cout << -1 << endl;else cout << r << endl;return 0;
}
P2371 [国家集训队] 墨墨的等式
奇妙好题!!
同余最短路:与差分约束有异曲同工之妙,都将约束条件转化为边,每种状态转化为点。把本来与图论毫不相干的问题抽象到具体的图上,通过拓扑排序,最短路等基础算法获得最小状态,从而解决问题。
在本题中,以 \(0\) 到 \(a_1-1\) 为节点,对于检点 \(v\),遍历数组 \(a\),将 \(v\) 和 \((v+a_j) \bmod a_1\) 连一条权值为 \(a_j\) 的边。把图建完之后,就形成了一个有向图,跑最短路后 \(dis_v\) 的值即为用题目给出的数能获得的最小的,对 \(a_1\) 取模为 \(v\) 的值。
$ \forall v$, \(1\) 到 n 中有 \(\frac{n-d i s_{v}}{a_{1}}+1\) 数是可以用题目给出的数到达,所以答案为 $\sum_{i}^{a_{1}-1} \frac{r-d i s_{v}}{a_{1}}- \frac{l-1-d i s_{v}}{a_{1}} $
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define DEBUG(x) cerr << #x << '=' << x << endlinline int rd()
{int x = 0, f = 1;char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = getchar();}while (ch >= '0' && ch <= '9')x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x * f;
}void print(int x)
{if (x < 0)putchar('-'), x = -x;if (x > 9)print(x / 10);putchar(x % 10 + '0');return;
}namespace Star_F
{#define int long longconst int maxn = 13, N = 5e5 + 10, M = 11 * 5e5 + 10;int n, l, r;int a[maxn];struct edge{int u, v, w, nxt;} e[M];int tot, head[N];void add(int u, int v, int w) { e[++tot] = {u, v, w, head[u]}, head[u] = tot; }struct node{int u, dis;bool operator<(const node &a) const { return dis > a.dis; }};int vis[N], dis[N];void init() { FOR(i, 1, N - 1)dis[i] = 1e18 + 10,vis[i] = 0; }void dj(int s){init();dis[s] = 0;priority_queue<node> q;q.push({s, dis[s]});while (!q.empty()){int u = q.top().u, k = q.top().dis;q.pop();if (vis[u] && k != dis[u])continue;vis[u] = 1;for (int i = head[u]; i; i = e[i].nxt){int v = e[i].v, w = e[i].w;if (dis[v] > dis[u] + w){dis[v] = dis[u] + w;q.push({v, dis[v]});}}}}int minn = 1e18 + 10, id = 0;int sov(int x){int ans = 0;FOR(i, 0, minn - 1){if (dis[i] <= x){ans += (x - dis[i]) / minn + 1;}}return ans;}void Main(){cin >> n >> l >> r;FOR(i, 1, n){cin >> a[i];if (a[i] == 0){i--;n--;continue;}if (minn > a[i])minn = a[i], id = i;}FOR(i, 0, minn - 1){FOR(j, 1, n){if (a[j] == minn)continue;add(i, (i + a[j]) % minn, a[j]);}}dj(0);int ans = sov(r) - sov(l - 1);cout << ans;}
}
signed main(){//freopen("inq.in","r",stdin);//freopen("inq.out","w",stdout);return Star_F::Main(), 0;return 0;
}
P2446 [SDOI2010] 大陆争霸
题解-bits
P8817 [CSP-S 2022] 假期计划
-
\(u\) 和 \(v\) 之间可达意为 \(u\) 和 \(v\) 之间可以不多于 \(k\) 次转车到达,及 \(u\),\(v\) 的距离不多于 \(k+1\)。
-
一个点 \(u\) 在家附近,意为 \(u\) 和 1 之间可达。
注意,为方便,特殊地,我们认为自己和自己不可达。
观察数据范围,\(n^2\) 可过,已经足够处理出每个点对之间的距离了。因此首先应该采用 BFS,\(n^2\) 计算任意点对之间是否可达。
注意:对于满足边权全为 1 的图,单源最短路可以做到 \(\mathcal{O}(n)\) 的复杂度(采用 BFS);
对于满足边权只有 0,1 两种的图,单源最短路也可以做到 \(\mathcal{O}(n)\) 的复杂度(采用 0-1 BFS)。
我们考虑一条 \(1 - a - b - c - d - 1\) 的路径,\(n^4\) 的枚举是不可行的。
很直接的想法是,依次考虑 \(a\),\(b\),\(c\),\(d\),发现贪心是不对的,从 1 选择了更大的权值景点 \(a\),但是可能接下来能到达的 \(b\) 就小的可怜;而选一个权值稍小一点的 \(a\),可能可达的 \(b\) 会很大。
但有一种贪心是对的,那就是确定了 \(a\),\(b\),\(c\),选择 \(d\) 的时候。如果我们确定了 \(c\),那么直接选择 \(c\) 可达的,在家附近的,不为 \(a\) 也不为 \(b\) 的权值最大的景点作为 \(d\) 即可。反正 \(d\) 之后没有景点了,所以可以直接贪心,没有后顾之忧。
由于环的对称性,可以发现确定了 \(b\),\(c\),\(d\) 之后,也可以贪心地选择 \(a\)。
有了大体思路。
我们定义 \(f(u)\) 表示 \(u\) 可达,且在家附近的权值最大的景点。
第一步:我们预处理出 \(f(u)\)。
第二步:直接 \(n^2\) 枚举,循环确定景点 \(b\) 和 \(c\)(\(b \ne c\)),然后记 \(a = f(b)\),\(d = f(c)\),然后试图用 \(w(a) + w(b) + w(c) + w(d)\) 更新答案,其中 \(w(u)\) 表示景点 \(u\) 的权值。
发现重复性的细节是存在问题的,比如:会出现 \(f(b) = c\) 的情况,此时让 \(a = f(b)\) 会导致 \(a = c\),这是不允许的。
不过,贪心思想还是不变的:\(a\) 应该尝试更换为 \(u\) 可达,且在家附近的权值第二大的景点。
更换定义,\(f(u, k)\) 表示 \(u\) 可达,且在家附近的权值第 \(k\) 大的景点。
当发现 \(f(b, 1) = c\) 时,将 \(a\) 设置为 \(f(b, 2)\) 即可。
当发现 \(f(c, 1) = b\) 时,将 \(d\) 设置为 \(f(c, 2)\) 即可。
发现并没有完全解决:如果这样处理后的 \(a\) 和 \(d\) 仍然重复怎么办?
还是贪心思想,考虑把 \(a\) 或者 \(d\) 换成更小的那个比较一下就行,具体来说,比如原先 \(a = f(b, 2)\),就把 \(a\) 下调为 \(f(b, 3)\);或者原先 \(d = f(c, 2)\),就把 \(d\) 下调为 \(f(c, 3)\),然后比较两种方案哪种更好就行了。
重复性解决了,但是还有个细节:如果原先 \(a = f(b, 1)\),发现 \(a = d\),我们想下调 \(a\)。是直接把 \(a\) 设置成 \(f(b, 2)\) 吗?错误,我们还需要检查一下 \(f(b, 2)\) 是否等于 \(c\),如果等于 \(c\) 还需要继续下调到 \(f(b, 3)\)。下调 \(d\) 同理。
至于原先 \(a = f(b, 2)\) 为啥下调 \(a\) 不需检查?因为如果原先 \(a = f(b, 2)\) 了说明 \(f(b, 1)\) 已经等于 \(c\) 了。所以 \(f(b, 3)\) 肯定什么问题都没有。
如果直接这么写是没有问题的,但是麻烦了。下面是一种更好写的处理方法:
直接分别枚举 \(a\) 分别作为 \(f(b, 1)\),\(f(b, 2)\),\(f(b, 3)\) 和 \(d\) 分别作为 \(f(c, 1)\),\(f(c, 2)\),\(f(c, 3)\) 组成的共九种情况,检查互异性然后更新答案即可。
需要提前预处理出 \(f(u, k \le 3)\),其实只要在最开始 BFS 预处理的同时维护一下就行了。
注意某些点可达,还在家附近的点数可能没有 3 个,因此注意类似访问 \(f(b, 3)\) 时产生的越界问题。
如果枚举到某个 \(b\),\(c\),发现 \(b\) 或者 \(c\) 有对应点数不超过 3 的情况时,这组 \(b\),\(c\) 不一定可以生成一组合法的解,属于正常现象。
题目保证全局上是有解的。
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;const int N = 2510, M = 20010;int n, m, k;
LL w[N];
int h[N], e[M], ne[M], idx;
int dist[N], f[N][4], q[N];
bool st[N][N];void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}void bfs(int start, int f[])
{int hh = 0, tt = 0;memset(dist, 0x3f, sizeof dist);dist[start] = 0;q[0] = start;while (hh <= tt){int t = q[hh ++ ];if (dist[t] == k + 1) continue;for (int i = h[t]; ~i; i = ne[i]){int j = e[i];if (dist[j] > dist[t] + 1){dist[j] = dist[t] + 1;st[start][j] = true;q[ ++ tt] = j;if (st[1][j]){f[3] = j;for (int u = 3; u; u -- ) if (w[f[u]] > w[f[u - 1]])swap(f[u], f[u - 1]);else break;}}}}
}int main()
{scanf("%d%d%d", &n, &m, &k);for (int i = 2; i <= n; i ++ ) scanf("%lld", &w[i]);memset(h, -1, sizeof h);while (m -- ){int a, b;scanf("%d%d", &a, &b);add(a, b), add(b, a);}for (int i = 1; i <= n; i ++ )bfs(i, f[i]);LL res = 0;for (int b = 2; b <= n; b ++ )for (int c = 2; c <= n; c ++ )if (st[b][c])for (int x = 0; x < 3; x ++ )for (int y = 0; y < 3; y ++ ){int a = f[b][x], d = f[c][y];if (a && d && a != d && a != c && b != d)res = max(res, w[a] + w[b] + w[c] + w[d]);}printf("%lld\n", res);return 0;
}