反向树状数组 + 离散化 HDU 5372

来源:互联网 发布:linux运维之道 mobi 编辑:程序博客网 时间:2024/06/06 03:27

Segment Game

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2086    Accepted Submission(s): 634


Problem Description
Lillian is a clever girl so that she has lots of fans and often receives gifts from her fans.

One day Lillian gets some segments from her fans Lawson with lengths of 1,2,3... and she intends to display them by adding them to a number line.At the i-th add operation,she will put the segment with length of i on the number line.Every time she put the segment on the line,she will count how many entire segments on that segment.During the operation ,she may delete some segments on the line.(Segments are mutually independent)
 

Input
There are multiple test cases.

The first line of each case contains a integer n — the number of operations(1<=n<=2105,n<=7105)

Next n lines contain the descriptions of the operatons,one operation per line.Each operation contains two integers a , b. 

if a is 0,it means add operation that Lilian put a segment on the position b(|b|<109) of the line.
(For the i-th add operation,she will put the segment on [b,b+i] of the line, with length of i.)

if a is 1,it means delete operation that Lilian will delete the segment which was added at the b-th add operation.
 

Output
For i-th case,the first line output the test case number.

Then for each add operation,ouput how many entire segments on the segment which Lillian newly adds.
 

Sample Input
30 00 30 150 10 01 10 10 0
 

Sample Output
Case #1:000Case #2:0102
Hint
For the second case in the sample:At the first add operation,Lillian adds a segment [1,2] on the line.At the second add operation,Lillian adds a segment [0,2] on the line.At the delete operation,Lillian deletes a segment which added at the first add operation.At the third add operation,Lillian adds a segment [1,4] on the line.At the fourth add operation,Lillian adds a segment [0,4] on the line
 
题意:在一个横轴上有两种操作

1. 第 i 个的输入,输入 0 x 表示在横轴 x 点处加一条长位 i 的线段,并输出这条线段里包含的线段数

2. 第 i 个的输入,输入 1 x 表示删除 第x条加入的线段

思路:这个题有个很特别的地方,就是线段是越来越长的。

所以你当前添加进来的这条线一定是最长的,那么其他的前段就只有3种情况,一种是一半在里面,一种是整段在里面,一种是全都不在里面



就像上面的图一样,那么就只考虑这个点右边的就行了,那么当前线段开始点右边的所有线段的开始点都在这个点右边,所有结束点也都在右边,那么结束点在当前线段结束点右边的线段都是要么一半在里面,要么全都不在里面的,所以结束点在当前结束点左边就是整段在里面的

简而言之,先把本线段开始点右边所有的开始点计数为 a (a = 右边所有线段数目),然后本线段结束点右边的所有结束点计数为 b (b = 右边不在全在本线段内的线段数) ,那么 a - b = 本线段覆盖的线段数了

首先一个问题是数据非常的大,要先离散化数据

离散化

先把所有坐标值存在数组里,然后数组排序,使用 unique 函数把相邻相同的刨去,得到一个长度len,那么你数据中的所有值都可以换成 1 到 len 的值了,用二分查找找到该值在数组中的出现下标,下标就可以作为替换值

所有准备工作处理完了,就该使用树状数组来记录和计算答案了。

反向树状数组

我们要找的是这个本线段起始点右边的所有起始点和结束点右边所有的结束点,所以很明显一个树状数组是搞不定的,要两个,一个存起始点,一个存结束点。然后就是另一个问题了,我们一般的树状数组记录的是前缀和,就只能统计出该点前面的点数,那要统计后面的点数,当然就是后缀和了,后缀和作法起始很简单,把树状数组反过来写就可以了:

void update(int x,int flag,int v){// 反向树状数组 while(x){tree[x][flag] += v;x -= lowbit(x);//正向的是 += lowbit 这里由于需要的是后缀,所以做反向的 }} int getSum(int x,int flag){int sum = 0;while(x <= cnt){sum += tree[x][flag]; //这里获取的就是 x及其后的后缀和 x += lowbit(x);}return sum;}
弄完之后就按操作顺序去操作就好了,答案就是 getSum(该段起始点,起始点) - getSum(该段结束点 + 1,结束点)

然后用另一个数组按顺序存下添加的线段,后面删除操作就很简单了

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;#define maxn 100010#define lowbit(x) (x & (-x))#define ll long long#define mem(a,x) memset(a,x,sizeof(a))int tree[maxn * 4][2];struct node{int s,e,flag;}p[maxn * 2],dele[maxn * 2];int n,len,cnt;int tm[maxn * 4];void update(int x,int flag,int v){// 反向树状数组 while(x){tree[x][flag] += v;x -= lowbit(x);//正向的是 += lowbit 这里由于需要的是后缀,所以做反向的 }} int getSum(int x,int flag){int sum = 0;while(x <= cnt){sum += tree[x][flag]; //这里获取的就是 x及其后的后缀和 x += lowbit(x);}return sum;}int search(int x){ // 用于离散化,查找这个值在区间里的下标,把下标作为值 int l = 1,r = cnt,mid;while(l <= r){mid = (l + r) >> 1;if(tm[mid] == x)return mid;if(tm[mid] < x)l = mid + 1;elser = mid - 1;}}int main(){int Case = 1;while(scanf("%d",&n) != EOF){cnt = len = 1;for(int i = 1;i <= n;i++){scanf("%d %d",&p[i].flag,&p[i].s);if(p[i].flag == 0){tm[cnt++] = p[i].s;tm[cnt++] = p[i].e = p[i].s + len; // 用tm数组把时间戳记录下来 len++;}}sort(tm + 1,tm + cnt); //把时间排序 cnt = unique(tm + 1,tm + cnt) - tm;//去除相邻同元素后的末尾下标for(int i = 1;i <= n;i++){if(p[i].flag == 1)continue;p[i].s = search(p[i].s);// 离散化 p[i].e = search(p[i].e);} mem(tree,0);int now = 1;printf("Case #%d:\n",Case++);for(int i = 1;i <= n;i++){if(p[i].flag == 1){update(dele[p[i].s].s,0,-1);//删除就是加-1 update(dele[p[i].s].e,1,-1);}else{dele[now++] = p[i]; //加进来的要作为后面删除的对象 printf("%d\n",getSum(p[i].s,0) - getSum(p[i].e + 1,1));update(p[i].s,0,1);update(p[i].e,1,1);}}}return 0;}

原创粉丝点击