HDU 6070 Dirt Ratio 分数规划 二分 线段树维护区间最值

链接

http://acm.hdu.edu.cn/showproblem.php?pid=6070

题意

n,(1<=n<=6104) 个数,每个数表示一个颜色,在所有合法的区间 [L,R] 中,设 X [L,R] 区间中不同颜色的个数, Y 为区间长度,求 min(X/Y)

思路

4天前cf刚刚打过这题,泥萌在想什么?(虽然窝不是这样做的23333,比赛的时候一发暴力加剪枝竟然不可思议的A了,代码贴在最后面

如题解所述,我们可以通过加入二分查找使这题变得和上面那道cf题一样。

二分答案 mid ,检验是否存在一个区间满足 size(l,r)rl+1mid ,也就是 size(l,r)+mid×lmid×(r+1)

利用上式建立线段树,线段树保存的是 size(l,r)+mid×l ,然后枚举右端点 r ,右端点 r 加入的时候带来了自己颜色的贡献,用一个数组记录下这个颜色上一次出现的位置,在相应的区间加上这个颜色的贡献就好了。然后询问 [1,i] 的最值,就是当前端点作为右端点 r 时可以得到的最值。

比赛的时候感觉精度只要 1e4 ,想想不用把答案完全算出来,算差不多就可以了,于是在之前写的暴力上加了个剪枝,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);
  }
}

你可能感兴趣的:(好题,线段树)