Codeforces Beta Round #6 (Div. 2 Only) 题解

113 阅读2分钟

比赛链接

A B C

D

思路

数据范围比较小,直接爆搜+剪枝。
剪枝1:如果当前攻击的次数大于等于已知的最小次数,得到的答案一定不会更优。
剪枝2:考虑攻击第ii个时,必须要把第i1i-1个打死,因为之后不管怎么攻击都不会再对第i1i-1个造成影响。所以攻击次数从max(0,(h[cur1]+b)/b)max(0,(h[cur-1]+b)/b)开始枚举(如果一直打第ii个,那么第i1i-1个被攻击在(h[cur1]+b)/b)(h[cur-1]+b)/b)次后死亡,如果第i1i-1个在开始攻击ii前就已经死亡,就从00开始枚举)。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
typedef long long ll;
using namespace std;
const ll N=20,INF=0x3f3f3f3f;
ll a,b,n,ans=INF;
ll h[N],rec[1000],hit[N];
void dfs(ll cur,ll tot)
{
	if(tot>=ans)
		return;
	if(cur==n)
	{
		rep(i,1,n)
			if(h[i]>=0)
				return;
		ans=tot;
		ll p=0;
		rep(i,1,n)
			rep(j,1,hit[i])
				rec[++p]=i;
		return;
	}
	ll cnt=0;
	int i=max(0ll,(h[cur-1]+b)/b);
	while(h[cur-1]>=0 || h[cur+1]>=0 || h[cur]>=0)
	{
		if(cnt<i)
		{
			h[cur]-=(i-cnt)*a;
			h[cur-1]-=(i-cnt)*b;
			h[cur+1]-=(i-cnt)*b;
			cnt=i;
		}
		hit[cur]=cnt;
		dfs(cur+1,tot+cnt);
		++i;
	}
	hit[cur]=0;
	h[cur]+=cnt*a;
	h[cur-1]+=cnt*b;
	h[cur+1]+=cnt*b;
}
int main()
{
	cin>>n>>a>>b;
	rep(i,1,n)
		cin>>h[i];
	dfs(2,0);
	cout<<ans<<endl;
	rep(i,1,ans)
		cout<<rec[i]<<" ";
	cout<<endl;
}

E

思路

首先想到的是枚举每一个区间,用区间最大值和区间最小值之差满足题意的区间维护答案。复杂度O(n3)O(n^3)
一步步优化,首先区间最大值和最小值可以通过线段树来维护查询,因为本题没有修改操作,所以ST表也可以。使用线段树每次查询的复杂度为O(logn)O(\log n),总的复杂度降为n2lognn^2\log n
当区间左端点确定时,区间最大值和最小值的差满足单调性,也就是随着右端点右移,这个差会不变或变大,但一定不可能变小。所以用二分来优化确定右端点的过程,复杂度降为O(nlog2n)O(n\log^2n)

代码

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define sf(a) scanf("%lld",&(a));
#define pf(a) printf("%lld\n",(a));
#define __ <<" "<<
#define pii pair<ll,ll> 
typedef long long ll;
using namespace std;
const ll N=1E5+10,INF=0x3f3f3f3f;
ll n,k,ans,cnt;
ll a[N],ma[4*N],mi[4*N],c[4*N];
pii rec[N];
void update(int p)
{
	ma[p]=max(ma[lc],ma[rc]);
	mi[p]=min(mi[lc],mi[rc]);
	c[p]=ma[p]-mi[p];
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		ma[p]=a[l];
		mi[p]=a[l];
		c[p]=0;
		return;
	}
	int mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	update(p);
}
pii query(int p,int l,int r,int al,int ar)
{
	if(al<=l && ar>=r)
		return make_pair(ma[p],mi[p]);
	int mid=(l+r)>>1;
	pii ret=make_pair(-INF,INF);
	if(al<=mid)
	{
		pii tmp=query(lc,l,mid,al,ar);
		ret.first=max(ret.first,tmp.first);
		ret.second=min(ret.second,tmp.second);
	}
	if(ar>mid)
	{
		pii tmp=query(rc,mid+1,r,al,ar);
		ret.first=max(ret.first,tmp.first);
		ret.second=min(ret.second,tmp.second);
	}
	return ret;
}
int main()
{
	sf(n)
	sf(k)
	rep(i,1,n)
		sf(a[i]);
	build(1,1,n);
#ifdef tst
	while(1)
	{
		int x,y;
		cin>>x>>y;
		cout<<query(1,1,n,x,y)<<endl;
	}
#endif
#ifndef tst
	rep(i,1,n)
	{
		ll l=i,r=n;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			pii que=query(1,1,n,i,mid);
			if(que.first-que.second>k)
				r=mid-1;
			else
				l=mid+1;
		}
		--l;
		ll dis=l-i+1;
		if(dis>ans)
		{
			ans=dis;
			rec[cnt=1]=make_pair(i,l);
		}
		else if(dis==ans)
			rec[++cnt]=make_pair(i,l);
	}
	cout<<ans __ cnt<<endl;
	rep(i,1,cnt)
		cout<<rec[i].first __ rec[i].second<<endl;
#endif
}