【BZOJ 2138】stone - Hall定理

  给一些互不包含的区间和一些石子堆,按顺序依次从区间内取走一些石子且每次有上限,要求每次都尽量取最多石子。


  假设前面的 i1 个区间全部满足了,且第 j 个区间取到了 kj ,现在要在第 i 个区间取石子。设当前区间会取 x 个石子,我们将每个区间拆成 ki 个点,划作 X 集,往石子堆可取的区间内的所有点连边。若第 k 堆石子有 ak 颗,则也拆成 ak 个点,划为 Y 集。若要使能取的条件全部成立,则意味着表示 X 端的点全部和 Y 端的点匹配。我们用Hall定理来处理,对于区间 i 以外的区间节点是已经满足了的,可以不用管;对于包含区间 i 的区间集合:若这些区间并起来不连续,那么显然可以拆成两个独立的连续区间而互不影响;若有两个区间的并包含第三个区间,则算上第三个区间的约束会比不算第三个区间的约束更紧。由此,因为区间均不包含,将当前所加入的区间全部排序后,我们只需要处理包含 i 的“区间的区间”的约束即可。设 p 为区间 i 的排名, si=rik=1ak,ti=li1k=1ak ki 为排名前 i 的区间所取到的石子的和,则约束可以表示成 srtlkrkl1+x,lpr ,可以得到 x 的上限是 min{srkr}max{tlkl1} 。不断插入区间然后线段树维护最值即可。
  时间复杂度 O(mlogm) ,但是不知道为什么在bzoj上跑的这么慢,明明只有4w的数据范围。。。
  哦数据里好像有 m<2 的要特判一下。


#include 
using namespace std;
#define rep(i,a,b) for (int i = a, _ = b; i <= _; i ++)

const int inf = 0x3fffffff;
const int maxn = 40007;
const int maxs = (1 << 17) + 7;

typedef int seg[maxs];
typedef int arr[maxn];

#define T int u, int l, int r
#define L lc , l , m
#define R rc , m + 1 , r
#define lc (u << 1)
#define rc (u << 1 | 1)

seg mn, mx, s1, s2, tg1, tg2;
arr a, l, r, k, h;
int v1, v2, v, ql, qr;
int n, m;

bool cmp(const int i, const int j) {
  return l[i] < l[j];
}

void upd(int u) {
  mn[u] = min(mn[lc], mn[rc]);
  mx[u] = max(mx[lc], mx[rc]);
}

void B(T) {
  mn[u] = inf, mx[u] = -inf;
  if (l == r) return;
  int m = (l + r) >> 1;
  B(L), B(R);
}

void tag1(int u, int v) {
  mn[u] -= v, s1[u] += v, tg1[u] += v;
}

void tag2(int u, int v) {
  mx[u] -= v, s2[u] += v, tg2[u] += v;
}

void push(int u) {
  if (tg1[u]) {
    tag1(lc, tg1[u]), tag1(rc, tg1[u]);
    tg1[u] = 0;
  }
  if (tg2[u]) {
    tag2(lc, tg2[u]), tag2(rc, tg2[u]);
    tg2[u] = 0;
  }
}

int get(T, int p) {
  if (l == r) return s1[u];
  int m = (l + r) >> 1; push(u);
  return p <= m ? get(L, p) : get(R, p);
}

void que(T) {
  if (ql <= l && r <= qr) {
    v1 = min(v1, mn[u]);
    v2 = max(v2, mx[u]);
    return;
  }
  push(u);
  int m = (l + r) >> 1;
  if (ql <= m) que(L);
  if (qr >  m) que(R);
}

void gao(T, int p) {
  if (l == r) {
    mn[u] = a[::r[h[l]]] - s1[u];
    mx[u] = a[::l[h[l]] - 1] - s2[u];
    return;
  }
  int m = (l + r) >> 1;
  push(u);
  if (p <= m) gao(L, p); else gao(R, p);
  upd(u);
}

void gao1(T) {
  if (ql <= l && r <= qr) {
    tag1(u, v);
    return;
  }
  int m = (l + r) >> 1; push(u);
  if (ql <= m) gao1(L);
  if (qr >  m) gao1(R);
  upd(u);
}

void gao2(T) {
  if (ql <= l && r <= qr) {
    tag2(u, v);
    return;
  }
  int m = (l + r) >> 1; push(u);
  if (ql <= m) gao2(L);
  if (qr >  m) gao2(R);
  upd(u);
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("in.txt", "r", stdin);
#endif
  int x, y, z, P;
  scanf("%d", &n);
  scanf("%d%d%d%d", &x, &y, &z, &P);
  rep (i , 1 , n) a[i] = ((i - x) * (i - x) % P + (i - y) * (i - y) % P + (i - z) * (i - z) % P) % P, a[i] += a[i - 1];
  //rep (i , 1 , n) cerr << a[i] << " "; cerr << endl;
  scanf("%d", &m);
  scanf("%d%d%d%d%d%d", &k[1], &k[2], &x, &y, &z, &P);
  rep (i , 3 , m) k[i] = (x * k[i - 1] % P + y * k[i - 2] % P + z) % P;
  rep (i , 1 , m) scanf("%d%d", &l[i], &r[i]);
  if (m < 2) {
    rep (i , 1 , m) printf("%d\n", min(a[r[i]] - a[l[i] - 1], k[i]));
    return 0;
  }
  rep (i , 1 , m) h[i] = i;
  B(1, 1, m);
  static arr rk;
  sort(h + 1, h + m + 1, cmp);
  rep (i , 1 , m) rk[h[i]] = i;
  rep (i , 1 , m) {
    int l = ::l[i], r = ::r[i], k = ::k[i], p = rk[i];
    int x = get(1, 1, m, p);
    v1 = a[r] - x;
    ql = p + 1, qr = m;
    if (ql <= qr)
      que(1, 1, m);
    int tmp = v1;
    v2 = a[l - 1] - x;
    ql = 1, qr = p - 1;
    if (ql <= qr)
      que(1, 1, m);
    v1 = tmp;
    v = min(k, min(v1 - v2, a[r] - a[l - 1]));
    printf("%d\n", v);
    ql = p, qr = m;
    gao1(1, 1, m);
    ql = p + 1 , qr = m;
    if (ql <= qr)
      gao2(1, 1, m);
    gao(1, 1, m, p);
  }
  return 0;
}

你可能感兴趣的:(线段树,贪心,Hall定理)