HDU 5372 Segment Game(树状数组)

 题意:每次插入一个线段,或删除一个已存在的线段,每次插入后输出当前插入的线段能完整覆盖存在的几条线段。

思路:一个线段被另一个线段只需要它的两个端点都在另一个线段之间即可。

用两个树状数组分别记录线段左右端点的位置,因为是按线段长度顺序插入的,所以只可能有以下五种情况

所以对于当前查询的区间[l, r]答案就是左端点在l之后的线段减去右端点在r之后的线段之差。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 400010;
//const int INF = 0x3f3f3f3f;
int n, m;
int c[2][MAXN];
int tmp[MAXN];
struct Line {
	int l, r;
} line[MAXN];
struct Op {
	int t, id;
} op[MAXN];
int lowbit(int x) {
	return x & -x;
}
void Update(int x, int d, int id) {
	while(x <= m) {
		c[id][x] += d;
		x += lowbit(x);
	}
}
int Sum(int x, int id) {
	int ret = 0;
	while(x > 0) {
		ret += c[id][x];
		x -= lowbit(x);
	}
	return ret;
}
int main() {
    //freopen("input.txt", "r", stdin);
	int kase = 0;
	while(cin >> n) {
		memset(c, 0, sizeof(c));
		int cnt = 0;
		for(int i = 0, t, u; i < n; i++) {
			scanf("%d", &t);
			op[i].t = t;
			if(!t) {
				scanf("%d", &u);
				line[cnt].l = u;
				line[cnt].r = u + cnt + 1;
				tmp[cnt<<1] = u;
				tmp[(cnt<<1)^1] = u + cnt + 1;
				op[i].id = cnt;
				cnt++;
			}
			else {
				scanf("%d", &u);
				u--;
				op[i].id = u;
			}
		}
		sort(tmp, tmp+cnt*2);
		m = unique(tmp, tmp+cnt*2) - tmp;
		for(int i = 0; i < cnt; i++) {
			line[i].l = lower_bound(tmp, tmp+m, line[i].l) - tmp + 1;
			line[i].r = lower_bound(tmp, tmp+m, line[i].r) - tmp + 1;
		}
		printf("Case #%d:\n", ++kase);
		cnt = 0;
		for(int i = 0, ans; i < n; i++) {
			if(!op[i].t) {
				ans = Sum(m, 0)-Sum(line[cnt].l-1, 0) - (Sum(m, 1)-Sum(line[cnt].r, 1));
				printf("%d\n", ans);
				Update(line[cnt].l, 1, 0);
				Update(line[cnt].r, 1, 1);
				cnt++;
			}
			else {
				Update(line[op[i].id].l, -1, 0);
				Update(line[op[i].id].r, -1, 1);
			}
		}
	}
    return 0;
}


















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值