[SDOI2010]粟粟的书架

嘟嘟嘟


哈,二合一题目。


刚开始数据范围没看清,满脑子在想二维主席树。越想越觉得不可做。
后来仔细的读了题,才发现这题前后50%的数据对应不同的做法。


对于\(R = 1\)的数据,就是主席树板子。用主席树维护权值的后缀和,然后贪心往权值大的二分。
需要注意的是相同权值的可能有多本书,要取大于等于\(h\)的最少数量。


对于剩下50%。令f[i][j][k]表示\((1, 1)\)\((i, j)\)的前缀矩阵中,权值大于等于\(k\)的书的高度之和;g[i][j][k]表示对应的数量之和。
然后二分,如果当前的和小于\(h\),就令L = mid - 1;否则R = mid。
同样需要注意的是相同高度的书可能有多个。还是要取大于\(h\)最少的书。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn1 = 205;
const int maxn2 = 5e5 + 5;
const int maxN = 1e3 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, m, q;

int a[maxn1][maxn1], Max = 0;
ll f[maxn1][maxn1][maxN];
int g[maxn1][maxn1][maxN];
In int solve(int xa, int ya, int xb, int yb, ll h)
{
  int L = 0, R = Max, ret = 0;
  while(L < R)
    {
      int mid = (L + R + 1) >> 1;
      ll Sum = f[xb][yb][mid] - f[xb][ya - 1][mid] - f[xa - 1][yb][mid] + f[xa - 1][ya - 1][mid];
      if(Sum < h) R = mid - 1;
      else L = mid;
    }
  if(L == 0) return -1;
  ret = g[xb][yb][L + 1] - g[xb][ya - 1][L + 1] - g[xa - 1][yb][L + 1] + g[xa - 1][ya - 1][L + 1];
  h -= f[xb][yb][L + 1] - f[xb][ya - 1][L + 1] - f[xa - 1][yb][L + 1] + f[xa - 1][ya - 1][L + 1];
  ll Sum = (f[xb][yb][L] - f[xb][ya - 1][L] - f[xa - 1][yb][L] + f[xa - 1][ya - 1][L]) - (f[xb][yb][L + 1] - f[xb][ya - 1][L + 1] - f[xa - 1][yb][L + 1] + f[xa - 1][ya - 1][L + 1]);
  int num = (g[xb][yb][L] - g[xb][ya - 1][L] - g[xa - 1][yb][L] + g[xa - 1][ya - 1][L]) - (g[xb][yb][L + 1] - g[xb][ya - 1][L + 1] - g[xa - 1][yb][L + 1] + g[xa - 1][ya - 1][L + 1]);
  ll pri = Sum / num;
  return ret + (h + pri - 1) / pri;
}
In void work0()
{
  for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= m; ++j) a[i][j] = read(), Max = max(Max, a[i][j]);
  for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= m; ++j)
      {
	  for(int k = 0; k <= Max; ++k)
	  {
	    f[i][j][k] = f[i][j - 1][k] + f[i - 1][j][k] - f[i - 1][j - 1][k];
	    g[i][j][k] = g[i][j - 1][k] + g[i - 1][j][k] - g[i - 1][j - 1][k];
	  }
	for(int k = 0; k <= a[i][j]; ++k) f[i][j][k] += a[i][j], ++g[i][j][k];
      }
  for(int i = 1; i <= q; ++i)
    {
      int xa = read(), ya = read(), xb = read(), yb = read();
      ll h = read();
      int ans = solve(xa, ya, xb, yb, h);
      if(ans < 0) puts("Poor QLW");
      else write(ans), enter;
    }
}

struct Tree
{
  int ls, rs;
  int num; ll sum;
}t[maxn2 * 20];
int root[maxn2], tcnt = 0;

In void insert(int old, int& now, int l, int r, int id)
{
  t[now = ++tcnt] = t[old];
  t[now].sum += id; ++t[now].num;
  if(l == r) return;
  int mid = (l + r) >> 1;
  if(id <= mid) insert(t[old].ls, t[now].ls, l, mid, id);
  else insert(t[old].rs, t[now].rs, mid + 1, r, id);
}
In int query(int old, int now, int l, int r, ll h)
{
  if(!now && h) return -INF;
  if(l == r)
    {
      ll Sum = t[now].sum - t[old].sum, pri = Sum / (t[now].num - t[old].num);
      if(Sum < h) return -INF;
      else return (h + pri - 1) / pri;
    }
  int mid = (l + r) >> 1;
  ll Sum = t[t[now].rs].sum - t[t[old].rs].sum;
  if(Sum > h) return query(t[old].rs, t[now].rs, mid + 1, r, h);
  else if(Sum == h) return t[t[now].rs].num - t[t[old].rs].num;
  else return t[t[now].rs].num - t[t[old].rs].num + query(t[old].ls, t[now].ls, l, mid, h - Sum);
}
  
int main()
{
  n = read(), m = read(), q = read();
  if(n ^ 1) {work0(); return 0;}
  for(int i = 1, x; i <= m; ++i) x = read(), insert(root[i - 1], root[i], 0, maxN - 5, x);
  for(int i = 1, L, R; i <= q; ++i)
    {
      read(), L = read(), read(), R = read();
      ll h = read();
      int ans = query(root[L - 1], root[R], 0, maxN - 5, h);
      if(ans < 0) puts("Poor QLW");
      else write(ans), enter;
    }
  return 0;
}
posted @ 2019-02-17 18:34  mrclr  阅读(98)  评论(0编辑  收藏  举报