http://acm.hdu.edu.cn/showproblem.php?pid=6070
给 n,(1<=n<=6∗104) 个数,每个数表示一个颜色,在所有合法的区间 [L,R] 中,设 X 为 [L,R] 区间中不同颜色的个数, Y 为区间长度,求 min(X/Y) 。
4天前cf刚刚打过这题,泥萌在想什么?(虽然窝不是这样做的23333,比赛的时候一发暴力加剪枝竟然不可思议的A了,代码贴在最后面
如题解所述,我们可以通过加入二分查找使这题变得和上面那道cf题一样。
二分答案 mid ,检验是否存在一个区间满足 size(l,r)r−l+1≤mid ,也就是 size(l,r)+mid×l≤mid×(r+1) 。
利用上式建立线段树,线段树保存的是 size(l,r)+mid×l ,然后枚举右端点 r ,右端点 r 加入的时候带来了自己颜色的贡献,用一个数组记录下这个颜色上一次出现的位置,在相应的区间加上这个颜色的贡献就好了。然后询问 [1,i] 的最值,就是当前端点作为右端点 r 时可以得到的最值。
比赛的时候感觉精度只要 1e−4 ,想想不用把答案完全算出来,算差不多就可以了,于是在之前写的暴力上加了个剪枝,AC = =。(完全想不到这样也行
#include
#include
#include
#include
using namespace std;
#define ll rt << 1
#define rr rt << 1 | 1
#define lson l, mid, ll
#define rson mid + 1, r, rr
template<class T1, class T2> inline void gmin(T1 &a, T2 b) { if (a > b) a = b; }
const int MAXN = 6e4 + 5;
const double EPS = 1e-5;
int n;
int a[MAXN];
int last_appear[MAXN];
double val[MAXN << 2], laz[MAXN << 2];
inline void pushUp(int rt) { val[rt] = min(val[ll], val[rr]); }
inline void pushDown(int rt) {
val[ll] += laz[rt]; laz[ll] += laz[rt];
val[rr] += laz[rt]; laz[rr] += laz[rt];
laz[rt] = 0;
}
void build(int l, int r, int rt, double v) {
laz[rt] = 0;
if (l == r) {
val[rt] = v * l;
return;
}
int mid = (l + r) >> 1;
build(lson, v);
build(rson, v);
pushUp(rt);
}
void update(int L, int R, int v, int l, int r, int rt) {
if (L <= l && r <= R) {
val[rt] += v;
laz[rt] += v;
return;
}
if (laz[rt] != 0) pushDown(rt);
int mid = (l + r) >> 1;
if (L <= mid) update(L, R, v, lson);
if (mid < R) update(L, R, v, rson);
pushUp(rt);
}
double query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return val[rt];
}
if (laz[rt] != 0) pushDown(rt);
int mid = (l + r) >> 1;
double ret = 1e5;
if (L <= mid) ret = query(L, R, lson);
if (mid < R) gmin(ret, query(L, R, rson));
return ret;
}
bool check(double mid) {
build(1, n, 1, mid);
for (int i = 1; i <= n; ++i) last_appear[i] = 0;
for (int i = 1; i <= n; ++i) {
update(last_appear[a[i]] + 1, i, 1, 1, n, 1);
last_appear[a[i]] = i;
if (query(1, i, 1, n, 1) <= mid * (i + 1)) return true;
}
return false;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
double l = 0, r = 1, mid, ans;
while (r - l > EPS) {
mid = (l + r) / 2;
if (check(mid)) r = (ans = mid) - EPS;
else l = mid + EPS;
}
printf("%.9f\n", ans);
}
}
#include
#include
#include
#include
#include
using namespace std;
template<class T1, class T2> inline void gmax(T1 &a, T2 b) { if (a < b) a = b; }
template<class T1, class T2> inline void gmin(T1 &a, T2 b) { if (a > b) a = b; }
typedef pair<int, int> P;
const int MAXN = 6e4 + 5;
const int INF = 0x3f3f3f3f;
const int EPS = 1e-5;
int n, cnt;
int a[MAXN], b[MAXN];
int F[2][MAXN], *f, *g;
int last_appear[MAXN];
int main() {
f = F[0]; g = F[1];
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
last_appear[a[i]] = 0;
f[i] = 1;
}
int mx = 0;
cnt = 0;
for (int i = 1; i <= n; ++i) {
if (last_appear[a[i]]) {
b[i] = i - last_appear[a[i]];
gmax(mx, b[i]);
}
else b[i] = INF, ++cnt;
last_appear[a[i]] = i;
}
if (cnt == 1) {
printf("%.9f\n", 1. / n);
continue;
}
else if (cnt == 2) {
if (n > 2 * (mx - 1)) printf("%.9f\n", 2. / n);
else printf("%.9f\n", 1. / (mx - 1));
continue;
}
//cout << "B:";
//for (int i = 1; i <= n; ++i) cout << " " << b[i];
//cout << endl;
int mn, d1 = cnt, d2 = n;
cnt = 0;
for (int j = 2; j <= n; ++j) {
mn = INF;
swap(f, g);
for (int i = j; i <= n; ++i) {
f[i] = g[i - 1] + (b[i] < j ? 0 : 1);
gmin(mn, f[i]);
++cnt;
}
//if (fabs(1. * mn / j - 1. * d1 / d2) < EPS) break;
if (mn * d2 < j * d1) {
d1 = mn;
d2 = j;
}
if (cnt > 2e8) break;
if (1. * d1 / d2 < EPS) break;
//cout << j << ":";
//for (int i = 1; i <= n; ++i) cout << " " << f[i];
//cout << endl;
}
printf("%.9f\n", 1. * d1 / d2);
}
}