【P6307】选择客栈 加强版

【P6307】选择客栈 加强版

Description

给定一个两个序列 \(A\)\(B\),求满足 \(i < j\)\(A_i = A_j\)\(\min\limits_{k = i}^j \leq p\) 的点对 \((i, j)\) 个数。

Limitations

序列长度不超过 \(10^6\)

Solution

又是点对题,套路做法是枚举右端点,然后想办法干掉左端点。

考虑对于一个右端点 \(r\),合法的左端点个数即为 \(r\) 左侧第一个费用不大于 \(p\) 的位置 \(x\) 的左侧的同颜色个数。可以考虑对每种颜色做一个前缀和维护前缀该颜色个数,然后查询 \(r\) 的颜色在 \(x\) 位置的前缀和值即可。时间复杂度 \(O(nk)\),可以通过 \(80\) 分的数据。当然可以使用可持久化线段树维护这些前缀和,但是这里考虑一种复杂度更低的做法。

注意到 \(p\) 是给定的,因此当右端点 \(r\) 增大时,位置 \(x\) 不会变小,而任何时刻大于 \(x\) 的位置的颜色个数是不需要统计的,因为不会产生贡献。因此我们只维护当前的 \(x\) 及其左侧的位置的每种颜色的个数即可。当碰到某个位置的费用不高于 \(p\) 时,则将该位置左侧所有没有被计算个数的位置都加入贡献,然后更新 \(x\) 即可。考虑时间复杂度,每个位置只会被计算一次贡献,因此总时间复杂度 \(O(n)\)

所以毒瘤鱼鱼为什么不把 k 出到与 n 同阶呢

Code

#include <cstdio>

const int maxn = 2000005;

int n, k, p;
ll ans;
int col[maxn], fee[maxn], cnt[maxn];

int main() {
  freopen("1.in", "r", stdin);
  qr(n); qr(k); qr(p);
  for (int i = 1; i <= n; ++i) {
    qr(col[i]); qr(fee[i]);
    if (fee[i] <= p) {
      for (int j = i - 1; fee[j] > p; --j) {
        ++cnt[col[j]];
      }
    }
    ans += cnt[col[i]];
    if (fee[i] <= p) {
      ++cnt[col[i]];
    }
  }
  printf("%lld\n", ans);
}

posted @ 2020-02-01 23:23  一扶苏一  阅读(225)  评论(0编辑  收藏  举报