因为对于 P P P 的定义已经给出很明显的提示了——多源最短路径,用 floyd。
然后就是本题的图的特殊之处:灰尘度的变化。
这个特殊之处直接导致了想一次 floyd 直接解决问题是不可能的。
因为 floyd 得到的最短路径抽象去了路径上的点,那样就不知道哪条最短路径会缩短了。
去思考本题的答案会发现:
天数越多,就越可能达标。
本题要求的是最少需要多少天。
然后就明白了大概的解题思路:二分搜索天数,用 floyd 判断这天的灰尘度是否达标。
接下来就是一些细节的问题了,例如对于完全图用二维数组存图、每轮搜索之前都需要根据天数初始化图的边权。
由于道路改善发生的次数越多,指标 P P P 越小。故根据这种单调性,可以采用二分,二分道路改善次数 x x x。已知道路改善次数的情况,我们可以很快的知道图上每一条边当下的边权,然后我们可以用 floyd 算法计算一下指标 P P P,判断指标 P P P 是否小于等于 Q Q Q 即可,复杂度 O ( n 3 log K ) O(n^3 \log K) O(n3logK),其中 K K K 为最大的道路改善次数,设成 1 0 9 10^9 109左右即可。
计算得到天数的范围是 0 0 0 到 1 0 7 10^7 107。
以及最重要的,关于数据范围的问题 q q q 的最大值已经超出了 int
所能达到的精度,应该用 long long
存储。
注:即使是 long long
,累加结束之后也可能溢出,故需要加上正数判断。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define lowbit(x) x & (-x)
#define endl "\n"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace fastIO {
inline int read() {
register int x = 0, f = 1;
register char c = getchar();
while (c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
inline void write(int x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
}
using namespace fastIO;
int w[105][105];
int ww[105][105];
int minn[105][105];
int n, q;
int check(int day) {
int ans = 0;
for(int i = 0; i < n; i ++) {
for(int j = 0; j < n; j ++) {
ww[i][j] = w[i][j];
}
}
for(int i = 0; i < n; i ++) {
int val = day / n + (day % n >= i + 1 ? 1 : 0);
for(int j = 0; j < n; j ++) {
ww[i][j] -= val, ww[j][i] -= val;
}
}
for(int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
ww[i][j] = max(minn[i][j], ww[i][j]);
}
}
for(int k = 0; k < n; k ++) {
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
ww[i][j] = min(ww[i][j], ww[i][k] + ww[k][j]);
}
}
}
for(int i = 0; i < n; i ++) {
for(int j = 0; j < n; j ++) {
ans += ww[i][j];
}
}
return ans;
}
int main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n = read(), q = read();
for(int i = 0; i < n; i ++) {
for(int j = 0; j < n; j ++) {
w[i][j] = read();
}
}
for(int i = 0; i < n; i ++) {
for(int j = 0; j < n; j ++) {
minn[i][j] = read();
}
}
int l = 0, r = 100000 * n, ans = -1;
while(l <= r) {
int mid = l + r >> 1;
if(check(mid) <= q) {
r = mid - 1;
ans = mid;
}
else {
l = mid + 1;
}
}
write(ans);
return 0;
}