BestCoder Round #66 (div.2) 1002.GTW likes gt

GTW likes gt

 
 Accepts: 54
 
 Submissions: 782
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
从前,有nn只萌萌的GT,他们分成了两组在一起玩游戏。他们会排列成一排,第ii只GT会随机得到一个能力值b_ibi。在第ii秒的时候,第ii只GT可以消灭掉所有排在他前面的和他不是同一组的且能力值小于他的GT。
为了使游戏更加有趣,GT的首领GTW会发功mm次,第ii次发功的时间为c_ici,则在第c_ici秒结束后,b_1,b_2,...,b_{c_i}b1,b2,...,bci都会增加1。
现在,GTW想知道在第nn秒之后,会有几只GT存活下来。
输入描述
第一行只有一个整数T(T\leq 5)T(T5),表示测试数据组数。
第二行有两个整数n,mn,m。表示GT的个数和GTW发功的次数。(1\leq n \leq 50000,1\leq m\leq 500001n50000,1m50000)
第三到n+2n+2行,每行有两个整数a_i,b_iai,bi,表示第ii只GT在哪个组和他的能力值 (0\leq a[i]\leq 1,1\leq b[i]\leq 10^6)(0a[i]1,1b[i]106)n+3n+3行到第n+m+2n+m+2行,每行有一个整数c_ici,表示GTW第ii次发功的时间。1\leq c[i]\leq n1c[i]n
输出描述
总共TT行,第ii行表示第ii组数据中,GT存活的个数。
输入样例
1
4 3
0 3
1 2
0 3
1 1
1
3
4
输出样例
3
Hint
11秒后 能力值为4\ 2\ 3\ 14 2 3 122秒后 能力值为4\ 2\ 3\ 14 2 3 133秒后 能力值为5\ 3\ 4\ 15 3 4 1,第22只GT被第33只GT消灭掉了
第44秒后 能力值为6\ 4\ 5\ 26 4 5 2
c_ici并不是有序的
题意:应该都可以看懂的,就不讲了。。。。

思路:用树状数组来实现任意区间更新,然后得到最终的序列(这样的好处是不用每过i秒再去判断a[i]前面有多少个会被其杀死),再利用逆序来保持最大值来寻找后面有多个能被其杀死,然后被杀死的就标记,最后统计没有被标记的个数就是答案了。

代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 51111
int n,m;
int bit[maxn],b[maxn];
int lowbit(int x){
	return x&(-x);
}
void update(int x,int v){
	while(x <= n){
		bit[x] += v;
		x += lowbit(x);
	}
}
int sum(int x)
{
	int s = 0;
	while(x > 0)
	{
		s += bit[x];
		x -= lowbit(x);
	}
	return s;
}
struct node
{
	int x,vis;
}a[maxn];
int main()
{
#ifdef CDZSC_June
	freopen("t.txt","r",stdin);
#endif
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		memset(bit,0,sizeof(bit));
		for(int i = 1; i<=n; i++)
		{
			scanf("%d%d",&a[i].vis,&a[i].x);
			update(i,a[i].x);
			update(i+1,-a[i].x);
			//把[i,i]的值设为a[i].x
		}
		for(int i = 1; i<=m; i++)
		{
			scanf("%d",&b[i]);
		}
		sort(b+1,b+m+1);
		for(int i = 1; i<=m; i++)
		{
			update(1,1);//把[1,n]区间都加上1
			update(b[i] + 1,-1);//把[b[i] + 1,n]区间都减去1
			//这两句代码合起来的作用是把[1,b[i]]区间都加上1
		}
		for(int i = 1; i<=n; i++)
		{
			a[i].x = sum(i);//求出区间更新后的各个点的值
		}
		int max0,max1,p;
		max0 = max1 = p= 0;
		for(int i = n; i>= 1; i--)//逆序的好处是保证max的下标总在a[i]的后面,以便用来判断a[i]是否会被杀
		{
			if(a[i].vis ==1){
				if(a[i].x > max1){
					max1 = a[i].x;
				}
				if(max0 > a[i].x){
					a[i].vis = 2;//意味着a[i]被杀死了,有可能a[i]被杀死多次;
					//但是我每次都把a[i].vis赋值为2,到最后只要统计非2的元素个数就好
				}
			}
			else if(a[i].vis == 0){
				if(a[i].x > max0){
					max0 = a[i].x;
				}
				if(max1 > a[i].x){
					a[i].vis = 2;
				}
			}
		}
		for(int i  = 1; i<=n; i++){
			if(a[i].vis != 2)
				p++;
		}
		printf("%d\n",p);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值