【做题】CERC2017B. Buffalo Barricades——时间倒流

原文链接 https://www.cnblogs.com/cly-none/p/CERC2017B.html

题意:在一个网格平面上,有\(n\)个点,其中第\(i\)个点在以\((x_i, y_i)\)为右上角的网格中。有\(m\)次操作,每次给出一个点\((x,y)\),表示从\((x,y)\)开始,向左和向下画线直到与之前画的线或坐标轴相交。这样会划分出以\((x,y)\)为右上角的新区域。你需要对每次操作求出,新区域中点的数量。

$n , m \leq 3 \times 10^5, ? 1 \leq x_i, y_i \leq 10^9, $ 每次操作的 \(x\) 和 \(y\) 分别互不相同。

首先,我们容易得出各种\(O(n \log ^ 2n)\)的数据结构做法。然而,它们并没有太大的启发意义。

考虑离线下来,处理每个点对所有操作的贡献。

考虑求出每个点和操作最后是被哪个操作控制的。也就是能直接覆盖这个点(或操作)的操作中时间最早的。这样会形成森林的结构。显然,一个点只会对它的祖先产生贡献。而它对某个祖先\(v\)产生贡献的冲要条件就是在它到\(v\)的路径上没有比\(v\)更早的操作。

求这个森林可以使用扫描线。按\(y\)从上往下扫描,每加入一个操作就删去在它前面的出现时间晚于它的操作。这可以用set来维护。

剩下的部分可以按时间从前往后枚举所有操作,每次答案就是它的子树和,然后切断它与父亲的边。方便起见,可以用时间倒流的技巧,就相当与每次合并一个点和它的父亲。用并查集维护即可。

时间复杂度\(O(n \log n)\)。

#include <bits/stdc++.h>
using namespace std;
#define gc() getchar()
template <typename tp>
inline void read(tp& x) {
  x = 0; char tmp; bool key = 0;
  for (tmp = gc() ; !isdigit(tmp) ; tmp = gc())
    key = (tmp == '-');
  for ( ; isdigit(tmp) ; tmp = gc())
    x = (x << 3) + (x << 1) + (tmp ^ '0');
  if (key) x = -x;
}
const int N = 300010, INF = 0x3f3f3f3f;
struct edge {
  int la,b;
} con[N << 1];
int tot,fir[N << 1];
void add(int from,int to) {
  con[++tot] = (edge) {fir[from],to};
  fir[from] = tot;
}
struct data {
  int x,y,v;
  bool operator < (const data& a) const {
    return y > a.y;
  }
} dat[N << 1];
int n,m,cnt,fa[N],val[N],uni[N],ans[N];
int getfa(int pos) {
  return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]);
}
typedef pair<int,int> pii;
set<pii> st;
set<pii> :: iterator t, t1;
void ins(int x,int y) {
  t = st.insert(pii(x,y)).first;
  while (t != st.begin()) {
    t1 = t;
    -- t1;
    if (t1 -> second < y) break;
    st.erase(t1);
  }
}
int ask(int p) {
  return st.upper_bound(pii(p+1,0))->second;
}
int main() {
  read(n);
  for (int i = 1, x, y ; i <= n ; ++ i) {
    read(x), read(y);
    -- x, -- y;
    dat[++cnt] = (data) {x,y,0};
  }
  read(m);
  for (int i = 1, x, y ; i <= m ; ++ i) {
    read(x), read(y);
    dat[++cnt] = (data) {x,y,i};
  }
  dat[++cnt] = (data) {INF,INF,m+1};
  sort(dat+1,dat+cnt+1);
  for (int i = 1, las = 1, tmp ; i <= cnt ; ++ i) {
    if (dat[i].y != dat[las].y) {
      for (int j = las ; j < i ; ++ j)
                if (dat[j].v) ins(dat[j].x, dat[j].v);
      las = i;
    }
    tmp = ask(dat[i].x);
    if (dat[i].v) fa[dat[i].v] = tmp;
    else ++ val[tmp];
  }
  for (int i = 1 ; i <= m ; ++ i)
    uni[i] = i;
  for (int i = m ; i >= 1 ; -- i) {
    ans[i] = val[getfa(i)];
    uni[getfa(i)] = getfa(fa[i]);
    val[getfa(fa[i])] += ans[i];
  }
  for (int i = 1 ; i <= m ; ++ i)
    printf("%d\n",ans[i]);
  return 0;
}

小结:这是一道技巧性比较强的数据结构题。利用询问间的关系解题的思路,还是有启发意义的。

原文地址:https://www.cnblogs.com/cly-none/p/CERC2017B.html

时间: 2024-10-01 03:05:13

【做题】CERC2017B. Buffalo Barricades——时间倒流的相关文章

USACO 做题小结

还记得之前,发过一篇阶段性总结与未来规划..结果由于最近rp爆发(保研成功+进wf)后者显然靠bin神,前者也是运气.因此,放松了一段时间.然后就开始刷usaco了,原因是不用花时间找解题报告在NOCOW上全部都有,很是方便.所以只需单独开一片随笔把每天做题总结一下. Chapter1-Getting started(入门) 都是超级大水题就略过了. Chapter2-Bigger Challenges(更大的挑战) 2.1 castle  这是一道基本的搜索题目,很基础.前面两个值直接搜的,后

大一下学期以来做题总结、

这段时间以来.除了搜索专题和KMP专题 其他专题都不怎么愿意想了. 就比如今天的水题. 代码敲错. 输出格式没看清题意. 看题又不仔细. 等交上去错一发之后在回过头来看题目. 而且看题这方面真的好差. 经常性题目没读完就开始去敲代码.在敲代码过程中再去看题意. 希望自己读题的时候要认真.争取把题目读准.读快. 还有一个坏习惯.做题的时候喜欢听歌.这样在做思维题的时候多少会有点影响把. 这样好了,以后在做题时间一定一定不要听歌. 还有就是思维方面比以前欠缺一点了. 可能是因为没有比赛时的感觉的了.

英语六级之段落信息匹配题型做题技巧

在今年改革的四六级题型中,阅读题里出现了一种"高端大气上档次"的题型--段落信息匹配题,占去了10%的分值.为此,很多考生都纷纷表示对这种题的担心.此次,新东方在线网络课堂四六级辅导名师赵建昆将对这种题型的备考方法和解题技巧进行解析,希望广大考生引起重视. 整个阅读部分占总分值的35%,而我们的考试时间只有forty minnutes,建昆老师建议大家在段落信息匹配题当中花去的时间千万不要超过15分钟,所以整体上看做阅读时需要注意一个很重要的问题:严控时间.  每一年考四六级的考生中,

【BZOJ做题记录】07.07~?

在NOI一周前重开一个坑 最后更新时间:7.07 11:26 7.06 下午做的几道CQOI题: BZOJ1257: [CQOI2007]余数之和sum:把k mod i写成k-k/i*i然后分段求后面的部分就好了 BZOJ1258: [CQOI2007]三角形tri:在草稿纸上按照位置和边找一下规律就好了 BZOJ1260: [CQOI2007]涂色paint:简单的区间DP BZOJ1303: [CQOI2009]中位数图:小于中位数的改为-1大于的改为1,算一算前缀和然后哈希一下乘一乘就好

做题细节

1. 如果题目是枚举的话,即最后化成十分简单的形式比较小, 可以直接将各种不同状态的结果运算过程写出来,但是这并不见得比写函数要快多少 而且比较容易出错,比如下标没有更改之类,这种错误比较烦人,因为你会查看算法, 但是算法本身并没有错误,所以如果复制粘贴的话,注意不同情况的不同点,如果自己 不够细心,最好写成函数的形式(注意判断边界),以防出错.//16:35 2004-4-17 2. 在编程之后检查的第一件事就是初始化, 你的初始化也许写在循环体之外,故只能AC一组测试数据,sample. /

ACM做题小技巧

1.一般用C语言节约空间,要用C++库函数或STL时才用C++; cout.cin和printf.scanf最好不要混用. 大数据输入输出时最好不要用cin.cout,防止超时. 2.有时候int型不够用,可以用long long或__int64型(两个下划线__). 值类型表示值介于 -2^63 ( -9,223,372,036,854,775,808) 到2^63-1(+9,223,372,036,854,775,807 )之间的整数. printf("%I64d",a); //_

FFT/NTT做题方法与调试技巧(+提高码题效率的一些想法)

(其实本文应该写成了"结合FFT讨论的调试技巧+码题方法",语文不好一写文章就偏题QAQ) 有意见欢迎提出,有遗漏欢迎补充! FFT(快速傅里叶变换)/NTT(数论变换)是卷积运算常见而实用的优化 但是FFT/NTT的处理过程并不像暴力运算(差不多是多项式乘法)那样能够直观地反映卷积结果的实时变化. 因此在使用FFT时将会或多或少地加大调试的难度. 如果调试程序时直接跟踪变量,每步手算结果比对,不仅会耽误大量时间,而且效果可能并不理想. 直接肉眼查错效率可能也不太高. 但也正由于FFT

l洛谷 P3926 SAC E#1 - 一道不可做题 Jelly

P3926 SAC E#1 - 一道不可做题 Jelly 题目背景 SOL君(炉石主播)和SOL菌(完美信息教室讲师)是好朋友. 题目描述 SOL君很喜欢吃蒟蒻果冻.而SOL菌也很喜欢蒟蒻果冻. 有一天,他们在一起搓炉石,而SOL菌则要拿出蒟蒻果冻招待他的客人. 蒟蒻果冻一般在a度下保存在冰箱里.但是刚拿出来的时候太冰了,需要加热.SOL菌打算用一种神奇的电炉加热蒟蒻果冻.根据观察,它有一个特点: 1.蒟蒻果冻小于c度的时候,每p单位时间加热1单位温度: 2.当蒟蒻果冻等于c度的时候,需要q单位

[AHOI2005]航线规划(树链剖分+时间倒流)

传送门 练一下树剖的板子,运用一下时间倒流和下放边权的思想. 题中所谓“关键航线”其实就是桥. 删边操作桥不好维护,但如果是加边,每加一条边,两点作为端点的这条路径就都不再是桥----->考虑时间倒流. 从后往前,每删除一条边,现在就是加边,该路径上所有边都不是桥(打上标记). 可以先求出一棵最小生成树(代码中是在dfs中实现的)那些多的边就以加边的方式加入(说明到最后一个操作后,这条路径的边也不是桥). #include<bits/stdc++.h> #define M 200003