codeforces985E 2000分dp

题目传送门

题意:

n个球,每个球上有一个数是ai,两个整数k和d。现在想让你把n个球分成若干堆,对于堆数没有限制,每堆包括至少k个球,每堆数中最大值减最小值不超过d。问你是否可以进行这样的分堆。

数据范围:1 <= k <= n <= 5e5 ,0 <= d <= 1e9 ,1 <= ai <= 1e9。

题解:

在想到当前堆分更多数可能使结果更优时,就应该意识到是dp。

dp[i] = 1表示前i个数是可以进行满足要求的分堆的,dp[i] = 0表示前i个数是不可以进行满足要求的分堆的。

0个数是合法的,这是起始状态,即dp[0] = 1。

把这n个数排序,从小到大遍历。

两个限制可以得出合法区间[L , R],L是满足aL >= ai - d最小的下标,R <= i - k。

如果dp[j] = 1 且 j∈[L , R],则dp[i] = 1。

找dp[j]的过程就是求dp数组在[L . R]区间最大值的过程。

我没有开dp数组,因为我把状态存线段树里面了。

感受:

初始状态设置时候忘记更改一个参数了,导致2A。

还是不够稳。

这种题当你意识到是dp时已经会做了,水平不同的选手意识到的时间不同,我还是一眼不了,得想一会。

确实水平提高了一些,以前碰到这种题,想都不想就去看题解了,现在能独立做出来了(虽说看了错误测例)。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 5e5 + 5 ;
int n , k , d ;
int a[maxn] ;
bool max1[maxn << 2] ;
int ls(int x)
{
    return x << 1 ;
}
int rs(int x)
{
    return x << 1 | 1 ;
}
void update(int id , int l , int r , int x , bool y)
{
	int mid = (l + r) / 2 ;
	if(l == r && l == x)
	{
		max1[id] = y ;
		return ;
	}
	if(x <= mid)
	  update(ls(id) , l , mid , x , y) ;
	else
	  update(rs(id) , mid + 1 , r , x , y) ;
	max1[id] = max(max1[ls(id)] , max1[rs(id)]) ;
}
bool query(int id , int l , int r , int x , int y)
{
    if(x > y)  return 0 ;
	bool ans = 0 ;
	int mid = (l + r) / 2 ;
	if(x <= l && r <= y)
	  return max1[id] ;
	if(x <= mid) ans = max(ans , query(ls(id) , l , mid , x , y)) ;
	if(y > mid)  ans = max(ans , query(rs(id) , mid + 1 , r , x , y)) ;
	return ans ;
}
void solve(int i)
{
	int l = lower_bound(a + 1 , a + i + 1 , a[i] - d) - a ;
	int r = i - k ;
	l -- ;
	l = max(l , 0) ;
	bool x = query(1 , 0 , n , l , r) ;
	if(x)  update(1 , 0 , n , i , 1) ;
}
int main()
{
	scanf("%d%d%d" , &n , &k , &d) ;
	for(int i = 1 ; i <= n ; i ++)  scanf("%d" , &a[i]) ;
	sort(a + 1 , a + n + 1) ;
	update(1 , 0 , n , 0 , 1) ;
	for(int i = 1 ; i <= n ; i ++)  
	  solve(i) ;
	if(query(1 , 0 , n , n , n))  printf("YES\n") ;
	else  printf("NO\n") ;
	return 0 ;
}
发布了187 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104080459