CodeForces 514D.R2D2 and Droid Army (线段树+双指针)

题意:有n(1<=n<=10^5)个机器人,每个机器人有m(1<=m<=5)个属性值(0<=属性值<=10^8),

然后你的大炮可以攻击k次,每次可以将所有机器人的同一个属性值-1(如果它大于0的话)

问如何选择可以使得连续被摧毁的机器人最多(所有属性值为0就看做被摧毁)。

得到 摧毁一个给定区间所需的最小k值,就是对于m个属性值,在区间内求最大值,这个可以用线段树做到。

关键是如何遍历所有的区间,来找出k炮可以消灭的最长区间。

CF上的题解是这么说的:对于区间[L,R),若可以摧毁,则让R++,若不可以则让L++,这样,记录沿途经过的所有区间,求里面的最大值就行了。

算是这么算,但是这样不太好理解。

我是这么理解的,遍历每一个L,来求以L为左端点,所能摧毁的最长区间[L,R].

求完之后,轮到L+1的时候,因为已知[L,R]是可以摧毁的,所以[L+1,R]一定是可以摧毁的,所以[L+1,r ]  ( r<R)  的所有区间都不可能是最优解,所以不需要考虑。

这样实际上在L++之后R根本不需要从L开始,而是从前一个R开始,于是就是前面所说的双指针,L与R都是单调递增,所以时间复杂度为O(n)*线段树的O(log2 n)。

总时间复杂度O(n*log2n)  (m太小,忽略)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring> 
using namespace std;

int n,m,k;
struct A{
	int a[5];
	A(){memset(a,0,sizeof(a));}
	void Input(){
		for(int i=0;i<m;++i) scanf("%d",&a[i]);
	}
	A max(A t1,A t2){
		for(int i=0;i<m;++i) a[i]=t1.a[i]>t2.a[i]?t1.a[i]:t2.a[i];
		return *this;
	}
	int Sum(){
		int sum=0;
		for(int i=0;i<m;++i) sum+=a[i];
		return sum;
	}
	void Show(){
		for(int i=0;i<m;++i){
			if(i) printf(" ");
			printf("%d",a[i]);
		}
		printf("\n");
	}
};
A st[100007 <<2];
void PushUp(int rt){
	st[rt].max(st[rt<<1],st[rt<<1|1]);
}
void Build(int l,int r,int rt){
	if(l==r){
		st[rt].Input();
		return;
	}
	int m=(l+r)>>1;
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	PushUp(rt);
}
A Query(int L,int R,int l,int r,int rt){
	if(L <= l && r <= R){return st[rt];}
	int m=(l+r)>>1;
	A X;
	if(L <= m) X.max(X,Query(L,R,l,m,rt<<1));
	if(R >  m) X.max(X,Query(L,R,m+1,r,rt<<1|1));
	return X;
}
int main(void)
{
	while(~scanf("%d%d%d",&n,&m,&k)){
		Build(1,n,1);
		int L=1,R=1;//区间 [L..R)
		A ANS;int Length=0; 
		A Cur;
		while(L <=n){
			if(L==R){
				if(R==n+1) break;
				else {++R;continue;}
			}
			Cur=Query(L,R-1,1,n,1);
			int S=Cur.Sum();
			if(S <=k&&R-L>Length){
				Length=R-L;
				ANS=Cur;
			}
			if(S <=k&&R <=n) ++R;
			else ++L;
		}
		ANS.Show();
	}
return 0;
}















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值