【BZOJ 2138】stone - Hall定理

4 篇文章 0 订阅
1 篇文章 0 订阅

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


  假设前面的 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 <bits/stdc++.h>
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值