码迷,mamicode.com
首页 > 其他好文 > 详细

CF1034D Intervals of Intervals

时间:2020-09-12 21:53:39      阅读:62      评论:0      收藏:0      [点我收藏+]

标签:for   operator   iter   print   cto   comet   int   要求   com   

题目链接

实际上时有题解的:zbk的题解

第一道CF 3500 的题。

这题的弱化版

弱化版的弱化版

还是根据诸多弱化版的思路,我们维护数轴上每个点(或者说每个“段”)最后一次被覆盖的时间,并在每个右端点上计算答案。对于一个右端点来说,记 \(f(p)\) 表示从 \(p\) 到右端点的并的长度。右端点向右扩展1,意味着右端点所覆盖的那些“段”的区间要永远被删除(贪心地想,以后的右端点都会包含新区间,那么那些被覆盖的部分就永远用不到了),新区间可以在 \(1...r\) 中起作用。发现就是区间修改操作。每次最多会分裂两个“段”,每个段最多被加入一次删除一次,那么总时间复杂度是 \(O(ng(n))\),其中 \(g(n)\) 表示用数据结构维护规模为 \(O(n)\) 的复杂度。用 set 和树状数组能做到 \(O(nlogn)\)

现在我们能快速求出 \(l...r\) 的并了,但是我们并没有“询问”,而是要求前 \(k\) 大。一看 \(k \le 10^9\) 就知道不会是超级钢琴模型,那么我们可以尝试二分出最小的那个并的长度,看看够不够 \(k\) 个。显然最小的那个并的长度限制越大,我们得到的并的个数就越少。这样,我们要做的就是求出答案小于等于 \(limi\) 的并的个数以及并的和。

发现并对于区间的区间的左右端点是单调的。那么我们可以考虑双指针维护以 \(r\) 为右端点的最靠右的 \(l\)。然后就 \(O(nlogLlogn)\)了。(\(L\) 为并的长度的值域)

然而我们发现我们所有查询操作的查询位置也是单调递增的,并且我们的树状数组只是用来维护个前缀和。那么我们可以随便搞搞就能砍掉那个树状数组的 \(log\)。不过还有个set 维护线段覆盖情况的 \(log\),这个也好砍,直接在二分外面用 \(vector\) 记录我们会进行的操作,二分里面直接扫 \(vector\) 即可。

总复杂度:\(O(nlogL)\)

关键代码:(实现时我学着 zbk 二分用的倍增写法)

int n, k;
struct node {
  int l, r, t;
  bool operator <(const node a) const { return l < a.l; }
};
set<node> st;
inline void Split(int pos) {
  set<node>::iterator it = st.lower_bound((node){pos, 0, 0});
  if (it == st.begin()) return ;
  --it;
  node nd = *it;
  if (nd.r <= pos)  return ;
  int l = nd.l, r = nd.r, t = nd.t;
  st.erase(it);
  st.insert((node){l, pos, t}); st.insert((node){pos, r, t});
}
vector<PII> opts[N];
ll tag[N], ans;
inline bool che(int limi) {
  memset(tag, 0, sizeof(tag));
  ll cnt = 0, tot = 0, nw = 0, sum = 0;
  int np = 0;
  for (register int i = 1; i <= n; ++i) {
    for (register uint j = 0; j < opts[i].size(); ++j) {
      int p = opts[i][j].fi, v = opts[i][j].se;
      if (p <= np) {
        sum += 1ll * (np - p + 1) * v;
        nw += v;
      } else {
        tag[p] += v;
      }
    }
    while (np != i && nw + tag[np + 1] >= limi)  ++np, nw += tag[np], sum += nw;
    tot += sum; cnt += np;
  }
  if (cnt < k)  return false;
  ans = tot - (cnt - k) * limi;
  return true;
}

int main() {
  read(n); read(k);
  for (register int i = 1; i <= n; ++i) {
    int l, r; read(l), read(r);
    Split(l), Split(r);
    bool fg = false;
    set<node>::iterator it, lstit = st.end();
    for (it = st.lower_bound((node){l, 0, 0}); it != st.end() && (*it).r <= r; lstit = it, ++it) {
      node nd = *it; int t = nd.t, len = nd.r - nd.l;
      opts[i].push_back(MP(1, -len)); opts[i].push_back(MP(t + 1, len));
      if (!fg)  fg = true;
      else  st.erase(lstit);
    }
    opts[i].push_back(MP(1, r - l));
    opts[i].push_back(MP(i + 1, -(r - l)));
    if (lstit != st.end()) st.erase(lstit);
		st.insert((node){l, r, i});
  }
  int s = 0;
  for (int d = 29; ~d; --d)
    if (che(s | (1 << d)))  s |= (1 << d);
  printf("%lld\n", ans);
  return 0;
}

CF1034D Intervals of Intervals

标签:for   operator   iter   print   cto   comet   int   要求   com   

原文地址:https://www.cnblogs.com/JiaZP/p/13594241.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!