【HDU】4866 Shooting 主席树

传送门:【HDU】4866 Shooting


题目大意:在一个射击游戏里面,游戏者可以选择地面上【1,X】的一个点射击,并且可以在这个点垂直向上射击最近的K个目标,每个目标有一个价值,价值等于它到地面的距离。游戏中有N个目标,每个目标从L到R,距离地面高度D。每次射击一个目标可以得到目标价值大小的分数,每次射击以后目标不会消失。如果在该点上方的目标个数小于可以射击的次数,那么就当多出来的次数全部射在该点上方最高的目标身上。(举个例子,如果你选择一个点x,可以向上射击a次,该点上方有b个目标,满足a>b,那么你得到的分数就是b个目标的分数之和再加上最上方的目标的分数*(a-b)),并且如果你前一次游戏得到的分数如果大于P,那么这次你得到的分数就翻倍。

现在给你N,M,X,P(1<=N, M ,X<=100000, P<=1000000000),表示有N个目标,M次游戏,坐标的范围【1,X】,分数可以翻倍的阀值。

接下来N行每行L,R,D,表示目标覆盖的范围L到R,高度为D。

接下来M行每行x,a,b,c。表示选择地面上坐标x的点射击。可以射击的次数根据公式K = ( a * Pre + b ) % c得到,其中Pre是上一次游戏得到的分数。


题目分析:

昨天学了主席树,就是为了写掉这题。。。

这题还算简单的吧。。对高度离散化后建立主席树,然后按照1到X的顺序依次插入该坐标上包含的线段的端点,利用标记的思想,左端点L标记+1,右端点R+1的位置-1,表示【L,R】区间内添加了一条线段。主席树同时维护两个信息:该区间的线段个数,该区间的线段分数和。

因为同一个坐标可能有三种情况:没有端点,一个端点,多个端点。

怎么处理呢?如果没有端点,那么直接将第X-1棵树的信息交给X即可。

如果一个端点,照样更新。

如果多个端点,那么第一次在第X-1棵树上更新,然后接下来都在自己的身上更新即可。

查询的时候,访问无需访问历史版本,因为按照插入线段的方式,我们恰好得到在坐标x上方的线段个数,而且还是按照D值从左到右排列的。

本题还有一个需要特别注意的地方就是,同一个坐标的同一高度可能会被多条线段覆盖,需要额外做一些处理。

比如你需要打到这个高度的a个目标,但是这个高度有b个目标,那么可以保证这个节点一定是叶子节点,因为只有一个高度,而高度是唯一的。那么如果a<=b,那么增加的价值就是这个节点的高度*a;如果a>b,那么这个节点一定是最高的那个高度,所以根据题意,增加的价值仍然是高度*a。

所以我们最后查找到叶子节点的时候,如果这个叶子节点是有线段的,那么就用高度*a即可。否则就是0。

那么本题至此已经解决了。

今天看群消息的时候,看到叉姐群里有这么一句话:对于OI党,题目一样A,good idea层出不穷。这就是年轻。大学的ACM反倒模式化了思维。

想想到觉得真的是这样,遇到不会的总是说这个算法我不会,然后再去学这相应的算法,从不自己去天马行空,唉,思维真的可能是模式化了吧?希望有一天我能够有所改变。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define REV( i , n ) for ( int i = n - 1 ; i >= 0 ; -- i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define FOV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define REPF( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define REPV( i , a , b ) for ( int i = a - 1 ; i >= b ; -- i )

#define CLR( a , x ) memset ( a , x , sizeof a )
#define mid() ( ( l + r ) >> 1 )

typedef long long LL ;

const int MAXN = 100005 ;
const int MAXE = 200005 ;

struct Edge {
	int h , c , n ;
	Edge () {}
	Edge ( int h , int c , int n ) :
		h ( h ) , c ( c ) , n ( n ) {}
} ;

struct Seg_Tree {
	int Ls , Rs ;
	int c ;
	LL val ;
} ;


Edge E[MAXE] ;
int H[MAXN] , cntE ;

Seg_Tree T[MAXN * 38] ;
int idx ;

int a[MAXN] , cnt ;

int Root[MAXN] ;
int N , M , X , P ;
LL Pre ;

//------------------------------------------------------
int unique ( int a[] , int n ) {
	int cnt = 1 ;
	sort ( a + 1 , a + n + 1 ) ;
	FOR ( i , 2 , n )
		if ( a[i] != a[cnt] )
			a[++ cnt] = a[i] ;
	return cnt ;
}

int lower_bound ( int key ) {
	int l = 1 , r = cnt + 1 ;
	while ( l < r ) {
		int m = mid () ;
		if ( a[m] >= key )
			r = m ;
		else
			l = m + 1 ;
	}
	return l ;
}
//------------------------------------------------------
int newnode () {
	return ++ idx ;
}

void build ( int &o , int l , int r ) {
	o = newnode () ;
	T[o].c = 0 ;
	T[o].val = 0 ;
	if ( l == r )
		return ;
	int m = mid () ;
	build ( T[o].Ls , l , m ) ;
	build ( T[o].Rs , m + 1 , r ) ;
}

int insert ( int old , int pos , int val , int c ) {
	int root = newnode () ;
	int now = root ;
	int l = 1 , r = cnt ;
	T[now].c = T[old].c + c ;
	T[now].val = T[old].val + val ;
	while ( l < r ) {
		int m = mid () ;
		if ( pos <= m ) {
			T[now].Ls = newnode () ;
			T[now].Rs = T[old].Rs ;
			now = T[now].Ls ;
			old = T[old].Ls ;
			r = m ;
		}
		else {
			T[now].Ls = T[old].Ls ;
			T[now].Rs = newnode () ;
			now = T[now].Rs ;
			old = T[old].Rs ;
			l = m + 1 ;
		}
		T[now].c = T[old].c + c ;
		T[now].val = T[old].val + val ;
	}
	return root ;
}

LL query ( int now , int kth ) {
	LL ans = 0 ;
	int l = 1 , r = cnt ;
	while ( l < r ) {
		int m = mid () ;
		if ( kth <= T[T[now].Ls].c ) {
			now = T[now].Ls ;
			r = m ;
		}
		else {
			ans += T[T[now].Ls].val ;
			kth -= T[T[now].Ls].c ;
			now = T[now].Rs ;
			l = m + 1 ;
		}
	}
	if ( T[now].c && kth )
		ans += T[now].val / T[now].c * kth ;
	return ans ;
}
//------------------------------------------------------
void init () {
	cntE = 0 ;
	CLR ( H , -1 ) ;
}

void addedge ( int x , int h , int c ) {
	E[cntE] = Edge ( h , c , H[x] ) ;
	H[x] = cntE ++ ;
}
//------------------------------------------------------
void solve () {
	int l , r , h ;
	init () ;
	idx = 0 ;
	cnt = 0 ;
	Pre = 1 ;
	FOR ( i , 1 , N ) {
		scanf ( "%d%d%d" , &l , &r , &h ) ;
		addedge ( l , h , 1 ) ;
		addedge ( r + 1 , h , -1 ) ;
		a[++ cnt] = h ;
	}
	cnt = unique ( a , cnt ) ;
	build ( Root[0] , 1 , cnt ) ;
	FOR ( x , 1 , X ) {
		if ( ~H[x] ) {
			int flag = 0 ;
			for ( int i = H[x] ; ~i ; i = E[i].n ) {
				h = E[i].h ;
				if ( !flag ) {
					Root[x] = insert ( Root[x - 1] , lower_bound ( h ) , E[i].c * h , E[i].c ) ;
					flag = 1 ;
				}
				else
					Root[x] = insert ( Root[x] , lower_bound ( h ) , E[i].c * h , E[i].c ) ;
			}
		}
		else {
			Root[x] = newnode () ;
			T[Root[x]] = T[Root[x - 1]] ;
		}
	}
	int pos , a , b , c ;
	REP ( i , M ) {
		scanf ( "%d%d%d%d" , &pos , &a , &b , &c ) ;
		int kth = ( a * Pre + b ) % c ;
		LL score = query ( Root[pos] , kth ) ;
		if ( Pre > P )
			score <<= 1 ;
		printf ( "%I64d\n" , score ) ;
		Pre = score ;
	}
}	

int main () {
	while ( ~scanf ( "%d%d%d%d" , &N , &M , &X , &P ) )
		solve () ;
	return 0 ;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值