2018 集训队互测 Day 1 简要题解

完美的队列:

如果能求出每个加入的数在所有队列中都被移出去的时刻,那就可以很方便地计算答案了。

对序列分块,先考虑整块的,更新跨过整个块的区间答案,记 bi b i 表示队列要弹出当前的数需要的容量,那么每次操作实际上是对 bi b i 进行区间减法,注意到大部分时候是对整块打标记,如果不是整块打标记直接暴力就行了。当 所有 bi<0 b i < 0 的时候,就是所有东西都被弹出去的时候,可以利用双指针更新答案。

离散的块实际上也可以类似处理,预处理跨过整块的东西的前缀和,同样枚举每个位置和跨过它的区间,然后类似整块也可以双指针求出答案,这部分细节详见代码。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 100005;
const int B = 325;
const int inf = 0x3f3f3f3f;

int n, m, ans, a[N], b[N], f[N], l[N], r[N], x[N], all[N], sum[N], pos[N], qry[N];
vector <int> v[N];

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m);
  for (int i = 1; i <= n; ++i) {
    Read(a[i]);
  }
  for (int i = 1; i <= m; ++i) {
    Read(l[i]), Read(r[i]), Read(x[i]);
  }
  for (int L = 1, R; L <= n; L += B) {
    R = min(L + B - 1, n);
    int val = -inf, tag = 0, cnt = 0, tot = 0;
    for (int i = L; i <= R; ++i) {
      b[i] = a[i], CheckMax(val, b[i]);
    }
    for (int i = 1, j = 2; i <= m; ++i) {
      if (i > 1 && val + tag <= 0) {
        if (l[i] <= L && R <= r[i]) {
          ++tag;
        } else if (l[i] <= R && L <= r[i]) {
          val = -inf;
          for (int k = L; k <= R; ++k) {
            if (k >= l[i] && k <= r[i]) {
              ++b[k];
            }
            CheckMax(val, b[k]);
          }
        }
      }
      for (; val + tag > 0 && j <= m; ++j) {
        if (l[j] <= L && R <= r[j]) {
          --tag;
        } else if (l[j] <= R && L <= r[j]) {
          val = -inf;
          for (int k = L; k <= R; ++k) {
            if (k >= l[j] && k <= r[j]) {
              --b[k];
            }
            CheckMax(val, b[k]);
          }
        }
      }
      sum[i] = sum[i - 1];
      if (l[i] <= L && R <= r[i]) {
        CheckMax(f[i], val + tag > 0 ? m + 1 : j - 1);
        ++sum[i], all[++tot] = i;
      } else if (l[i] <= R && L <= r[i]) {
        qry[++cnt] = i, pos[cnt] = tot;
      }
    }
    for (int i = L; i <= R; ++i) {
      for (int j = 1, k = 2, val = a[i]; j <= cnt; ++j) {
        if (j > 1) {
          val += sum[qry[j]] - sum[qry[j - 1]];
          if (l[qry[j]] <= i && i <= r[qry[j]]) {
            ++val;
          }
        }
        for (; val > 0 && k <= cnt; ++k) {
          val -= sum[qry[k]] - sum[qry[k - 1]];
          if (l[qry[k]] <= i && i <= r[qry[k]]) {
            --val;
          }
        }
        if (l[qry[j]] <= i && i <= r[qry[j]]) {
          if (val > 0) {
            CheckMax(f[qry[j]], sum[m] - sum[qry[cnt]] < val ? m + 1 : all[pos[cnt] + val]);
          } else {
            CheckMax(f[qry[j]], l[qry[k - 1]] <= i && i <= r[qry[k - 1]] ? val ? all[pos[k - 1] + val + 1] : qry[k - 1] : all[pos[k - 1] + val]);
          }
        }
      }
    }
  }
  memset(sum, 0, sizeof sum);
  for (int i = 1; i <= m; ++i) {
    if (!sum[x[i]]++) {
      ++ans;
    }
    v[f[i]].pb(x[i]);
    for (auto x : v[i]) {
      if (!--sum[x]) {
        --ans;
      }
    }
    printf("%d\n", ans);
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

完美的集合:

注意到合法的关键点连成了一棵树,所以可以容斥,用 i i 合法的减去 i pari p a r i 都合法的。

枚举之后将树处理出来,变成了一棵有根树,可以利用DFS序DP求出最大权值以及方案数。

剩下就是组合数取模的问题了,就是要快速求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值