【BZOJ 4662】 4662: Snow (线段树+并查集)

4662: Snow

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 136  Solved: 47

Description

2333年的某一天,临冬突降大雪,主干道已经被雪覆盖不能使用。城

主 囧·雪 决定要对主干道进行一次清扫。

临冬城的主干道可以看为一条数轴。囧·雪 一共找来了n个清理工,第

i个清理工的工作范围为[li,ri],也就是说这个清理工会把[li,ri]这一

段主干道清理干净(当然已经被清理过的部分就被忽略了)。当然有可能主

干道不能全部被清理干净,不过这也没啥关系。

虽然 囧·雪 啥都不知道,但是他还是保证了不会出现某一个清理工的

工作范围被另一个清理工完全包含的情况(不然就太蠢了)。

作为临冬城主,囧·雪 给出了如下的清扫方案:

在每一天开始的时候,每一个还没有工作过的清理工会观察自己工作

范围内的道路,并且记下工作范围内此时还没有被清理的道路的长度(称

为这个清理工的工作长度)。然后 囧·雪 会从中选择一个工作长度最小的

清理工(如果两个清理工工作长度相同,那么就选择编号小的清理工)。然

后被选择的这个清理工会清理自己的工作范围内的道路。为了方便检查工

作质量,囧·雪 希望每一天只有一个清理工在工作。

你要注意,清理工的工作长度是可能改变的,甚至有可能变成0。尽管

如此,这个清理工也还是会在某一天工作。

现在,囧·雪 想要知道每一天都是哪个清理工在工作?

Input

第一行两个整数t,n。分别表示主干道的长度(也就是说,主干道是数

轴上[1,t]的这一段)以及清理工的人数。

接下来n行,每行两个整数li,ri。意义如题。

n<=3*10^5, 1<=li<ri<=t<=10^9,保证输入的li严格递增

Output

输出n行,第i行表示第i天工作的清理工的编号。

Sample Input

15 4

1 6

3 7

6 11

10 14

Sample Output

2

1

3

4

HINT

Source

【分析】

  其实这个也是线段树的经典了吧。

  之前做过好几题,就是每次操作的时候都会有一个节点删掉以后彻底没用,这样即使暴力也是不会超时的,因为每个点只会用一次。

  这里的这个点就是每个清洁工,我们只会询问他一次。

  跳出清扫区间这个圈子,用清洁工来建线段树。

  因为当你修改某段区间的时候,受影响的清洁工也是一个区间。

  先离散化,然后暴力删去区间,用并查集快速跳去已经失效的区间,每个位置只会操作一次,是可以过的。

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Maxn 300010
  8 #define INF 0x7fffffff
  9
 10 int nl[Maxn],nr[Maxn],nn[Maxn*2];
 11
 12 struct node
 13 {
 14     int l,r,lc,rc,mn,lazy;
 15     int id;
 16 }tr[Maxn*2];
 17
 18 int rt[Maxn*2];
 19 int rtt(int x)
 20 {
 21     if(rt[x]!=x) rt[x]=rtt(rt[x]);
 22     return rt[x];
 23 }
 24
 25 int len=0;
 26 int build(int l,int r)
 27 {
 28     int x=++len;
 29     tr[x].l=l;tr[x].r=r;
 30     if(l!=r)
 31     {
 32         int mid=(l+r)>>1;
 33         tr[x].lc=build(l,mid);
 34         tr[x].rc=build(mid+1,r);
 35         tr[x].lazy=0;
 36         int lc=tr[x].lc,rc=tr[x].rc;
 37         if(tr[lc].mn<=tr[rc].mn) tr[x].mn=tr[lc].mn,tr[x].id=tr[lc].id;
 38         else tr[x].mn=tr[rc].mn,tr[x].id=tr[rc].id;
 39     }
 40     else tr[x].lc=tr[x].rc=0,tr[x].mn=nn[nr[l]]-nn[nl[l]],tr[x].id=l;
 41     return x;
 42 }
 43
 44 void upd(int x)
 45 {
 46     if(tr[x].l==tr[x].r) return;
 47     int lc=tr[x].lc,rc=tr[x].rc;
 48     if(tr[lc].mn!=INF) tr[lc].mn-=tr[x].lazy;
 49     if(tr[rc].mn!=INF) tr[rc].mn-=tr[x].lazy;
 50     tr[lc].lazy+=tr[x].lazy;
 51     tr[rc].lazy+=tr[x].lazy;
 52     tr[x].lazy=0;
 53 }
 54
 55 void change(int x,int y,int p)
 56 {
 57     if(tr[x].mn==INF) return;
 58     int mid=(tr[x].l+tr[x].r)>>1,lc=tr[x].lc,rc=tr[x].rc;
 59     if(p)
 60     {
 61         if(nl[tr[x].l]<=y&&y<nr[tr[x].l]&&nl[tr[x].r]<=y&&y<nr[tr[x].r])
 62         {
 63             tr[x].lazy+=nn[y+1]-nn[y];
 64             tr[x].mn-=nn[y+1]-nn[y];
 65             return;
 66         }
 67         if(tr[x].l==tr[x].r) return;
 68         upd(x);
 69         if(y<nr[mid]) change(lc,y,p);
 70         if(nl[mid+1]<=y) change(rc,y,p);
 71     }
 72     else
 73     {
 74         if(tr[x].l==tr[x].r)
 75         {
 76             tr[x].mn=INF;
 77             return;
 78         }
 79         upd(x);
 80         if(y<=mid) change(tr[x].lc,y,p);
 81         else change(tr[x].rc,y,p);
 82     }
 83     if(tr[lc].mn<=tr[rc].mn) tr[x].mn=tr[lc].mn,tr[x].id=tr[lc].id;
 84     else tr[x].mn=tr[rc].mn,tr[x].id=tr[rc].id;
 85 }
 86
 87
 88 struct nnode{int x,id;}t[Maxn*2];
 89 bool cmp(nnode x,nnode y) {return x.x<y.x;}
 90
 91 int main()
 92 {
 93     int mx,n;
 94     scanf("%d%d",&mx,&n);
 95     for(int i=1;i<=n;i++) {scanf("%d%d",&nl[i],&nr[i]);
 96                       t[i*2-1].x=nl[i];t[i*2-1].id=i;t[i*2].x=nr[i];t[i*2].id=-i;}
 97     sort(t+1,t+1+2*n,cmp);
 98     mx=1;nl[t[1].id]=1;nn[1]=t[1].x;
 99     for(int i=2;i<=2*n;i++)
100     {
101         if(t[i].x!=t[i-1].x) mx++,nn[mx]=t[i].x;
102         if(t[i].id>0) nl[t[i].id]=mx;
103         else nr[-t[i].id]=mx;
104     }
105     build(1,n);
106     for(int i=1;i<=mx;i++) rt[i]=i;
107     for(int i=1;i<=n;i++)
108     {
109         int x=tr[1].id;
110         printf("%d\n",x);
111         change(1,x,0);
112         int st=rtt(nl[x]);
113         for(int j=st;j<nr[x];)
114         {
115             change(1,j,1);
116             rt[j]=rtt(j+1);
117             j=rt[j];
118         }
119     }
120     return 0;
121 }

2017-03-27 09:29:36

时间: 2024-08-05 01:44:06

【BZOJ 4662】 4662: Snow (线段树+并查集)的相关文章

【XSY2707】snow 线段树 并查集

题目描述 有\(n\)个人和一条长度为\(t\)的线段,每个人还有一个工作范围(是一个区间).最开始整条线段都是白的.定义每个人的工作长度是这个人的工作范围中白色部分的长度(会随着线段改变而改变).每一天开始时你要选择一个人满足这个人的工作长度最小(如果有多个就选编号最小的).把这个人的工作区间染黑.请你输出每天你选了哪个人. 保证工作范围中左端点和右端点单调递增. \(n\leq 300000\) 题解 先把线段离散化成很多个小区间,那么每个小区间只会被染黑一次(染黑之后不会变白). 因此每次

UVALive 4730 Kingdom 线段树+并查集

题目链接:点击打开链接 题意见白书P248 思路: 先把读入的y值都扩大2倍变成整数 然后离散化一下 用线段树来维护y轴 区间上每个点的 城市数量和联通块数量, 然后用并查集维护每个联通块及联通块的最大最小y值,还要加并查集的秩来记录每个联通块的点数 然后就是模拟搞.. T^T绝杀失败题..似乎数组开小了一点就过了,== #include<stdio.h> #include<math.h> #include<vector> #include<string.h>

UVA 1455 - Kingdom(线段树+并查集)

UVA 1455 - Kingdom 题目链接 题意:给定一些城市坐标点,连在一起的城市称为一个州,现在用两种操作,road表示把城市a,b建一条路,line表示询问一个y轴上穿过多少个州,和这些州共包含多少个城市 思路:利用并查集维护每个州的上界和下界还有城市个数,然后每次加进一条路的时候,根据两个集合的位置可以处理出区间的州和城市数如何进行加减,然后利用线段树搞就可以了 代码: #include <cstdio> #include <cstring> #include <

(线段树+并查集) Codeforces Round #416 (Div. 2) E Vladik and Entertaining Flags

In his spare time Vladik estimates beauty of the flags. Every flag could be represented as the matrix n?×?m which consists of positive integers. Let's define the beauty of the flag as number of components in its matrix. We call component a set of cel

【BZOJ-3673&amp;3674】可持久化并查集 可持久化线段树 + 并查集

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status][Discuss] Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Input 5 6

UVALive 4730 线段树+并查集

点击打开链接 题意:在坐标上给n个点,r的操作是将两个点连起来,l的操作是问你y=u的这条线连接的集合块数和这些集合内的点的个数 思路:很麻烦的一道题,在网上看了题意和做法后,开始了一下午的调bug过程,做法很好懂,我开了两个线段树,一个维护点代表的直线的集合个数,另一个则是路过集合内的点的个数,然后集合的判断直接用并查集就行了,这是两个核心,然后就是自己瞎写的了,代码丑的可以而且好像除了本人别人看着可能要骂人了,有兴趣研究的可以留言我来解答,那难的部分其实就是并查集合并时该怎么将这两个要维护的

【Codeforces811E】Vladik and Entertaining Flags [线段树][并查集]

Vladik and Entertaining Flags Time Limit: 20 Sec  Memory Limit: 512 MB Description n * m的矩形,每个格子上有一个数字代表颜色. q次询问,询问[l, r]有几个连通块,若颜色相同并且连通则属于同一个连通块. Input 输入第一行n,m,q. 然后一个n*m的矩形. 之后q行,每行两个整数l,r. Output 输出q行,对于每个询问输出答案. Sample Input 4 5 4 1 1 1 1 1 1 2

【CF687D】Dividing Kingdom II 线段树+并查集

[CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两个集合,使得这个划分的代价最小,问最小代价是什么.一个划分的代价是指,对于所有两端点在同一集合中的边,这些边的边权最大值.如果没有端点在同一集合中的边,则输出-1. $n,q\le 1000,m\le \frac {n(n-1)} 2,w_i\le 10^9$ 题解:先考虑暴力的做法,我们将所有边按

[HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]

题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个点,得到一堆联通块,我们要做的就是用原图中剩下的边把这些联通块穿起来 考虑这个点$u$在$MST$上的位置,可以知道有两种边:一种是从$u$的任意一个儿子的子树连到$u$的子树外面的,一种是在$u$的两个儿子的子树之间连接的 第一种情况: 考虑边$(u,v)$,没有进入$MST$中,那么若它是某个节