Hihocoder #1384 Genius ACM___枚举+倍增

题目大意:

给定 T T 组数据,每组数据给出 n,m,k n , m , k ,有 N N 个数 A1A2...An1,An A 1 , A 2 , . . . , A n − 1 , A n ,按原顺序,将其划分成几个区间,并保证每个区间里,任意取出 M M 对数其差的平方和小于k ,如果取不出 m m 对就尽量多取,问最少能划分成多少个区间。

T12 T ≤ 12
1n,m5×105 1 ≤ n , m ≤ 5 × 10 5
0k1018 0 ≤ k ≤ 10 18
0Pi220 0 ≤ P i ≤ 2 20

分析:

固定区间左侧,类似倍增的思想枚举右侧,
设当前的左侧是 l l ,然后 [l,l+2r] [ l , l + 2 r ] 是合法的, [l,l+2r+1] [ l , l + 2 r + 1 ] 是不合法的,
那么在我们此时在 [l+2r,l+2r+1] [ l + 2 r , l + 2 r + 1 ] 二分
然后每个区间的合法就是要满足那个小于 k k ,就是就是最大的 m m 个数跟最小的 m m 个数一一对应然后计算得到的结果小于 k k ,因为这样计算最大,而最大值小于 k k 则这个区间合法。

代码:

#include
#include
#include
#include
#include
#define N 500005

using namespace std;

typedef long long ll;

int n, m, tot;
ll a[N], b[N];
ll k;

bool cmp(int x, int y)
{
    return a[x] < a[y];
}

bool Check(int l, int r)
{
    int num = 1;
    while(l + num - 1 <= r) b[num] = a[l + num - 1], num++;
    num--;
    sort(b + 1, b + num + 1);
    int x = 1, y = num, cnt = 0;
    ll cp = 0;
    while (x < y && cnt < m)
    {
        cp += (b[y] - b[x]) * (b[y] - b[x]);
        if (cp > k) return 0;
        x++, y--, cnt++;
    }
    return cp <= k;
}

bool Work(int id)
{
    int x = 1, y = tot, cnt = 0;
    ll res = 0;
    while (x < y && cnt < m)
    {
        cnt++;
        while (x < y && b[x] > id) x++;
        while (x < y && b[y] > id) y--;
        if (x >= y) break;
        res += (a[b[y]] - a[b[x]]) * (a[b[y]] - a[b[x]]);
        x++, y--;
        if (res > k) return 0;
    }
    return res <= k;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
           scanf("%d %d %lld", &n, &m, &k);
           for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
           int ans = 0, l = 1;
           while (l <= n)
           {
                  int num = 1;
                  while (l + num <= n && Check(l, l + num)) num <<= 1;
                  int x = l + num / 2, y = l + num;
                  if (y > n) y = n;

                  tot = 0;
                  for (int i = l; i <= y; i++) b[++tot] = i;
                  sort(b + 1, b + tot + 1, cmp);

                  int now = x;
                  while (x <= y)
                  {
                         int mid = (x + y) >> 1;
                         if (Work(mid)) now = mid, x = mid + 1;
                                   else y = mid - 1;
                  }
                  l = now + 1;
                  ans++;
           }
        printf("%d\n", ans);
    }
    return 0;
}

你可能感兴趣的:(暴力/枚举/模拟,C++,倍增)