【集训】最短路!
最短路
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 << endl
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;
}
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;
else
ans += 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;
else
cout << 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;
else
l = mid + 1;
}
if(l==flag+1)
cout << "AFK" << endl;
else
cout << 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 <= m;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;
else
printf("%.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 << endl
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;
}
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 long
const 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] 大陆争霸
由题可知,有的城市被保护。设 \(u\) 被 \(v\) 保护,我们从 \(v\) 建一条有向边到 \(u\) ,并记录 \(u\) 的入度 \(in_u\) 。广度遍历图,在摧毁 \(v\) 时,删去 \(v\) 到 \(u\) 的边,并更新入度。当 \(in_u=0\) 时,方可进入城市 \(u\) 。
设 \(arrive_i\) 表示到达 \(i\) 的时间(可能要在门口等待)。设 \(into_i\) 为进入 \(i\) 的时间(即什么时候所有保护 \(i\) 的城市被摧毁了)。设 \(dis_i\)表示摧毁 \(i\) 的时间,可得 \(dis_i=\max (arrive_i, into_i)\) 。设 \(\left\{j_{n}\right\}\) 为 \(i\) 的所有前驱,则 \(into_i=\max \{into_{j_n} \}\)。
#include <bits/stdc++.h>
using namespace std;
const int N=3001,M=200001;
struct edge{
int u,nxt,v;
}e[M];
int he[N],n,m,tote;
struct Graph{
int u,nxt;
}G[M];
int hg[N],totg;
struct node{
int key,len;
friend bool operator < (node x,node y){
return x.len<y.len;
}
};
int dis[N],into[N],in[N],arr[N];
bool vis[N];
priority_queue<node> q;
void dijkstra(int s){
for(int i=1;i<=n;i++)
dis[i]=arr[i]=1e9;
dis[s]=into[s]=arr[s]=0;
in[s]=0;
q.push({s,0});
int u,v;
while(!q.empty()){
u=q.top().key;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int k=he[u];k;k=e[k].nxt){
v=e[k].u;
if(dis[u]+e[k].v<arr[v]){
arr[v]=dis[u]+e[k].v;
if(!in[v]){
dis[v]=max(into[v],arr[v]);
q.push({v,-dis[v]});
}
}
}
for(int k=hg[u];k;k=G[k].nxt){
v=G[k].u;
into[v]=max(into[v],dis[u]);
in[v]--;
if(!in[v]){
dis[v]=max(into[v],arr[v]);
q.push({v,-dis[v]});
}
}
}
}
int main(){
cin >> n >> m;
while(m--){
int x,y,l;
cin >> x >> y >> l;
e[++tote].u = y, e[tote].nxt = he[x], e[tote].v = l, he[x] = tote;
}
for(int i=1;i<=n;i++){
int x, y;
cin >> x;
while(x--){
cin >> y;
++in[i], G[++totg].u = i, G[totg].nxt = hg[y], hg[y] = totg;
}
}
dijkstra(1);
cout << dis[n] << endl;
return 0;
}
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;
}