「一本通 1.2 练习 2」扩散
显然联通块的个数是随时间越来越少的。
所以可以二分时间。
经过一波运算,可以得出两点需要联通的时间是
(abs(x[i] - x[j]) + abs(y[i] - y[j]) + 1) / 2
然后每次用并查集维护一下联通分块就好了。
第一次写这种开结构体的并查集,感觉很酷炫。
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;const int MAXN = 60;
int dis[MAXN][MAXN], x[MAXN], y[MAXN];
int maxt, n;struct Disjoint_Set
{int f[MAXN], sum;void init() { _for(i, 1, n) f[i] = i; sum = n;}int find(int x){if(f[x] != x)f[x] = find(f[x]);return f[x];}void Union(int a, int b) { int aa = find(a), bb = find(b);if(aa != bb){f[aa] = bb;sum--;}}
}S;bool check(int t)
{S.init();_for(i, 1, n)_for(j, i + 1, n)if(dis[i][j] <= t)S.Union(i, j);return S.sum == 1;
}int main()
{scanf("%d", &n);_for(i, 1, n) scanf("%d%d", &x[i], &y[i]);_for(i, 1, n)_for(j, i + 1, n){int d = (abs(x[i] - x[j]) + abs(y[i] - y[j]) + 1) / 2;dis[i][j] = dis[j][i] = d;maxt = max(maxt, d);}int l = -1, r = maxt;while(l + 1 < r){int m = (l + r) >> 1;if(check(m)) r = m;else l = m;} printf("%d\n", r);return 0;
}
poj 2018
这道题是一道好题。
首先可以二分平均数,把最优问题转化成判定性问题
然后可以预处理,把每个数减去平均数,只要最终的结果大于等于0就可以了,这个很秀。
最后要解决一个问题,求一个子序列,它的和最大,子序列的程度不小于L
我们可以枚举终点i,然后就转化成0 <= j <= i - L中 最小的[j, i - L]
每次枚举的时候j的范围只会加1,所以我们可以直接在枚举的时候更新答案。
注意最后输出要转化一下类型。
#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;const int MAXN = 1e5 + 10;
double a[MAXN], s[MAXN];
int n, L;bool check(double m)
{ _for(i, 1, n) s[i] = s[i - 1] + a[i] - m;double ans = -1e12, min_val = 1e12; _for(i, L, n){min_val = min(min_val, s[i - L]);ans = max(ans, s[i] - min_val);}return ans >= 0;
}int main()
{scanf("%d%d", &n, &L);_for(i, 1, n) scanf("%lf", &a[i]); double l = -2001, r = 2001;while(r - l > 1e-8){double m = (l + r) / 2;if(check(m)) l = m;else r = m;}printf("%d\n", int(r * 1000));return 0;
}