UVa 1451 平均值 数形结合

题意:给定一个长度为n 的01串,然后选一个长度至少为L的子串,使得子串的平均值最大。

分析:
不会做,学习一下。可以参考这篇论文:http://wenku.baidu.com/link?url=Mz_53YzQ6hJLkXpIS9v3Uo3k9CGF4hgkcSzY5EhV5XbsF3HkW2Ae4EGCXaIdm4380TneqShe63xuTRJvHebPcPAKdUKuIRWkM04LcjSv2nK
树形结合,将字符串的每个位置对应成xOy坐标上的点,那么平均指即为两点之间的斜率。
然后维护一个相邻两点斜率递增的一个集合q(即凸点集),然后后面枚举的点只需要在这个集合中找切点即为该点满足题意的最优斜率。注意比如从1~5,有3个‘1’,除数不是5-1=4,要注意它占了1,2,3,4,5,5个位置,所以除数是5,所以枚举两点的时候要注意。

#include<cstdio>
#include<iostream>
using namespace std;

const int maxn = 100000 + 5;

int n, L;
char s[maxn];
int sum[maxn], p[maxn]; // average of i~j is (sum[j]-sum[i-1])/(j-i+1)

// compare average of x1~x2 and x3~x4
int compare_average(int x1, int x2, int x3, int x4) {
  return (sum[x2]-sum[x1-1]) * (x4-x3+1) - (sum[x4]-sum[x3-1]) * (x2-x1+1);
}

int main() {
  int T;

  scanf("%d", &T);

  while(T--) {
    scanf("%d%d%s", &n, &L, s+1);

    sum[0] = 0;
    for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + s[i] - '0';

    int ansL = 1, ansR = L;

    // p[i..j) is the sequence of candidate start points
    int i = 0, j = 0;
    for (int t = L; t <= n; t++) { // end point

      while (j-i > 1 && compare_average(p[j-2], t-L, p[j-1], t-L) >= 0) j--; // remove concave points 删去上凸点,
      p[j++] = t-L+1; // new candidate

      while (j-i > 1 && compare_average(p[i], t, p[i+1], t) <= 0) i++; // update tangent point

      // compare and update solution
      int c = compare_average(p[i], t, ansL, ansR);
      if (c > 0 || c == 0 && t - p[i] < ansR - ansL) {
        ansL = p[i]; ansR = t;
      }
    }
    printf("%d %d\n", ansL, ansR);
  }
  return 0;
}
posted @ 2016-05-16 22:50  HARD_UNDERSTAND  阅读(177)  评论(0编辑  收藏  举报