题解:UVA12125 March of the Penguins
涉及知识点:网络流,拆点。
解题思路
由于企鹅的跳动非常像网络流的流,考虑可以把企鹅的移动过程抽象成一个流网络。
因为有很多冰块上有企鹅,所以建立一个超级源点,把有源点向冰块连一条边,容量为当前冰块上企鹅的数量。
把最终的终点看做汇点,由于本题的汇点不确定,而且数据范围很小,所以可以枚举汇点。
再考虑冰块之间如何连边,如果两个冰块可以相互跳跃,即两个冰块的距离是在企鹅可以跳跃距离的范围内的,就可以连一条双向边,也就是互相连边。
现在思考如何将次数限制加到流网络中,如果把冰块的起跳次数直接加到当前冰块的出边上,会发现可能会超过冰块起跳的限制,所以不能这样做。
因为这个问题是对点有流量限制,所以我们考虑如何拆点。
把每一个点都拆成一个入点和一个出点。
现在,我们就可以把到这个点的边连到这个点的入点上,把离开这个点的边连到出点上,当然,入点到出点也要连一条边,这条边的容量为这个冰块的可以起跳的次数,这样是为了限制从这个冰块起跳的企鹅数量。
这样,本题的图就建立完成了。
以下分析本流网络的可行流是不是与原问题的解是一一对应的。
我们可以把每只企鹅跳过的边的流量加一,这样就得到了一个可行流。
判断可行流只需要判断两点,一是流量守恒,还有一个是容量限制。
容量限制显然是满足的,因为刚才我们已经把所以限制都加到了流网络里了。
流量守恒也是满足的,因为企鹅不可能在除了源点和汇点停留的,所以流量也是守恒的。
反推回去也是可以得到一个合法解的,所以原问题任何一个企鹅跳跃的方案都是可以对应到我们建立的流网络里的可行流。
如何判断一个方案是不是合法的呢?相当于判断流网络里的企鹅是不是都能流到汇点,直接求一下最大可行流是不是企鹅的总数就可以了,如果是,就说明当前汇点是一个可行点。
这样的话就可以通过本题了。
代码
#include <bits/stdc++.h>
//#define int long long
#define ll __int128
#define db double
#define ldb long double
#define vo void
#define endl '\n'
#define il inline
#define re register
#define ve vector
#define p_q priority_queue
#define PII pair<int, int>
#define u_m unordered_map
#define bt bitset
#define iv il void
#define ii il int
#define ib il bool
#define ri register int
using namespace std;
using i64 = long long;
//#define O2 1
#ifdef O2
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast","inline")
#endif
//#define use_fast
namespace IO {
#define IOSIZE 100000
int precision = 3, POW[10] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
char ibuf[IOSIZE], obuf[IOSIZE], *p1 = ibuf, *p2 = ibuf, *p3 = obuf;
#ifdef ONLINE_JUDGE
#define getchar()((p1==p2)and(p2=(p1=ibuf)+fread(ibuf,1,IOSIZE,stdin),p1==p2)?(EOF):(*p1++))
#define putchar(x)((p3==obuf+IOSIZE)&&(fwrite(obuf,p3-obuf,1,stdout),p3=obuf),*p3++=x)
#define isdigit(ch)(ch>47&&ch<58)
#define isspace(ch)(ch<33)
#endif
template<typename T>inline T read() {
T s = 0;
int w = 1;
char ch;
while (ch = getchar(), !isdigit(ch) && (ch != EOF))if (ch == 45)w = -1;
if (ch == EOF)return 0;
while (isdigit(ch))s = s * 10 + ch - 48, ch = getchar();
return s * w;
} template<typename T>inline bool read(T&s) {
s = 0;
int w = 1;
char ch;
while (ch = getchar(), !isdigit(ch) && (ch != EOF))if (ch == 45)w = -1;
if (ch == EOF)return 0;
while (isdigit(ch))s = s * 10 + ch - 48, ch = getchar();
return s *= w, 1;
} inline bool read(char&s) {
while (s = getchar(), isspace(s));
return 1;
} inline bool read(char*s) {
char ch;
while (ch = getchar(), isspace(ch));
if (ch == EOF)return 0;
while (!isspace(ch))*s++ = ch, ch = getchar();
*s = '\000';
return 1;
} template<typename T>inline void print(T x) {
if (x < 0)putchar(45), x = -x;
if (x > 9)print(x / 10);
putchar(x % 10 + 48);
} inline void print(char x) {
putchar(x);
} inline void print(char*x) {
while (*x)putchar(*x++);
} inline void print(const char*x) {
for (int i = 0; x[i]; i++)putchar(x[i]);
} inline bool read(std::string&s) {
s = "";
char ch;
while (ch = getchar(), isspace(ch));
if (ch == EOF)return 0;
while (!isspace(ch))s += ch, ch = getchar();
return 1;
} inline void print(std::string x) {
for (int i = 0, n = x.size(); i < n; i++)putchar(x[i]);
} inline bool read(bool&b) {
char ch;
while (ch = getchar(), isspace(ch));
b = ch ^ 48;
return 1;
} inline void print(bool b) {
putchar(b + 48);
} inline bool read(double&x) {
int a = 0, b = 0;
char ch = getchar();
bool f = 0;
while (ch < 48 || ch > 57) {
if (ch == 45)f = 1;
ch = getchar();
}
while (47 < ch && ch < 58) {
a = (a << 1) + (a << 3) + (ch ^ 48);
ch = getchar();
}
if (ch != 46) {
x = f ? -a : a;
return 1;
}
ch = getchar();
while (47 < ch && ch < 58) {
b = (b << 1) + (b << 3) + (ch ^ 48), ch = getchar();
}
x = b;
while (x > 1)x /= 10;
x = f ? -a - x : a + x;
return 1;
} inline void print(db x) {
if (x < 0) {
putchar(45), x = -x;
}
x = round((ldb)x * POW[precision]) / POW[precision], print((int)x), putchar(46), x -= (int)(x);
for (int i = 1; i <= precision; i++)x *= 10, putchar(x + 48), x -= (int)x;
} template<typename T, typename...T1>inline int read(T&a, T1&...other) {
return read(a) + read(other...);
} template<typename T, typename...T1>inline void print(T a, T1...other) {
print(a), print(other...);
} struct Fast_IO {
~Fast_IO() {
fwrite(obuf, p3 - obuf, 1, stdout);
}
} io;
template<typename T>Fast_IO&operator>>(Fast_IO&io, T&b) {
return read(b), io;
} template<typename T>Fast_IO&operator<<(Fast_IO&io, T b) {
return print(b), io;
}
#ifdef use_fast
#define cout io
#define cin io
#endif
}
const int inf = 1e8;
const db PI = acos(-1);
namespace Template {
int fact[200000];
int Triangle[1010][1010];
int Prime[2000000], Prime_vis[2000000];
int Prime_len;
void Fact(int n, int mod) {
fact[0] = 1;
for (int i = 1; i <= n; i ++ ) fact[i] = ((fact[i - 1]) % mod * (i % mod)) % mod;
}
void Pascal_s_triangle(int n, int mod) {
for (int i = 0; i <= n; i ++ ) Triangle[i][0] = 1;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= i; j ++ )
Triangle[i][j] = (Triangle[i - 1][j] + Triangle[i - 1][j - 1]) % mod;
}
void Get_prime(int n) {
for (int i = 2; i <= n; i ++ ) {
if (!Prime_vis[i]) Prime[++Prime_len] = i;
for (int j = 1; Prime[j] * i <= n; j ++ ) {
Prime_vis[Prime[j] * i] = 1;
if (i % Prime[j] == 0) break;
}
}
}
int pw(int x, int y, int mod) {
int res = 1;
while (y) {
if (y & 1) res = ((res % mod) * (x % mod)) % mod;
x = (x % mod) * (x % mod) % mod;
y >>= 1;
}
return res;
}
int pw(int x, int y) {
int res = 1;
while (y) {
if (y & 1) res *= x;
x *= x;
y >>= 1;
}
return res;
}
int GCD(int x, int y, int mod) {
return __gcd(x, y) % mod;
}
int LCM(int x, int y, int p) {
return x * y % p * pw(GCD(x, y, p), p - 2, p) % p;
}
int C(int n, int m, int mod) {
if (m > n || m < 0) return 0;
return fact[n] * pw(fact[m], mod - 2, mod) % mod * pw(fact[n - m], mod - 2, mod) % mod;
}
il int Lucas(int n, int m, int p) {
if (!m) return 1;
return (C(n % p, m % p, p) * Lucas(n / p, m / p, p)) % p;
}
int Ask_triangle(int x, int y) {
return Triangle[x][y];
}
il int BSGS(int a, int b, int p) {
if (1 % p == b % p) return 0;
int k = sqrt(p) + 1;
u_m<int, int> Hash;
for (int i = 0, j = b % p; i < k; i ++ ) {
Hash[j] = i;
j = j * a % p;
}
int ak = 1;
for (int i = 0; i < k; i ++ ) ak = ak * a % p;
for (int i = 1, j = ak; i <= k; i ++ ) {
if (Hash.count(j)) return i * k - Hash[j];
j = j * ak % p;
}
return -inf;
}
il int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
il int exBSGS(int a, int b, int p) {
b = (b % p + p) % p;
if (1 % p == b % p) return 0;
int x, y;
int d = exgcd(a, p, x, y);
if (d > 1) {
if (b % d) return -inf;
exgcd(a / d, p / d, x, y);
return exBSGS(a, b / d * x % (p / d), p / d) + 1;
}
return BSGS(a, b, p);
}
il int inv(int a, int p) {
return pw(a, p - 2, p);
}
}
using namespace Template;
#ifdef use_fast
using namespace IO;
#else
#define IOS 1
#endif
//#define fre 1
#define multitest 1
const int N = 200 + 10;
const int M = 20400 + 10;
const int Mod = 1e9 + 9;
const db eps = 1e-8;
namespace zla {
#define x first
#define y second
int n, S, T;
db D;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];
PII p[N];
bool check(PII a, PII b) {
double dx = a.x - b.x, dy = a.y - b.y;
return dx * dx + dy * dy < D * D + eps;
}
void add(int a, int b, int c) {
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs() {
int hh = 0, tt = 0;
memset(d, -1, sizeof d);
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt) {
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i]) {
int ver = e[i];
if (d[ver] == -1 && f[i]) {
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if (ver == T) return true;
q[ ++ tt] = ver;
}
}
}
return false;
}
int find(int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i;
int ver = e[i];
if (d[ver] == d[u] + 1 && f[i]) {
int t = find(ver, min(f[i], limit - flow));
if (!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int Dinic() {
int r = 0, flow;
while (bfs()) while (flow = find(S, inf)) r += flow;
return r;
}
il void Work() {
//入点:1 ~ n
//出点:n + 1 ~ 2 * n
memset(h, -1, sizeof h);
idx = 0;
cin >> n >> D;
S = 0;
int tot = 0;
for (int i = 1; i <= n; i ++ ) {
int x, y, a, b;
cin >> x >> y >> a >> b;
p[i] = {x, y};
add(S, i, a);
add(i, i + n, b);
tot += a;
}
for (int i = 1; i <= n; i ++ )
for (int j = i + 1; j <= n; j ++ )
if (check(p[i], p[j])) {
add(n + i, j, inf);
add(n + j, i, inf);
}
int cnt = 0;
for (int i = 1; i <= n; i ++ ) {
T = i;
for (int j = 0; j < idx; j += 2) {//还原流网络
f[j] += f[j ^ 1];
f[j ^ 1] = 0;
}
if (Dinic() == tot) {
cnt ++;
cout << i - 1 << " ";
}
}
if (cnt == 0) cout << -1;
cout << endl;
}
il void main() {
Work();
}
}
il void Init() {
;
}
signed main() {
int T;
#ifdef IOS
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#endif
#ifdef fre
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
#endif
Init();
#ifdef multitest
cin >> T;
#else
T = 1;
#endif
while (T--) zla::main();
return 0;
}
/*
*/
注:由于无法在洛谷提交代码,笔者已在 AcWing
上通过此题,AcWing
题号:\(2278\)。