[BZOJ4828][DP]HNOI2017:大佬(cyk)

版权声明:虽然博主很菜,但是还是请注明出处(我觉得应该没人偷我的博客) https://blog.csdn.net/qq_43346903/article/details/88957931

BZOJ4828

观察发现,我们加血和攻击是两个互不影响的操作,只要保证我们时刻存活就可以进行攻击,而攻击又有一些约束条件,方便起见,我们设置三个子任务 s u b t a s k subtask

  • s u b t a s k 1 subtask1: 处理出,保证自己存活的情况下,可以有多少天自由行动(不回血)
  • s u b t a s k 2 subtask2: 在完成 s u b t a s k 1 subtask1 后处理出,自由行动的时候,花多少天能够对大佬造成多少伤害,要具体到每一个可能数值上,因为只能把血打成0,这里只考虑攻击一次
  • s u b t a s k 3 subtask3: 在完成 s u b t a s k 2 subtask2 后,我们能否击败每个大佬

对于 s u b t a s k 1 subtask1 ,一个简单dp即可
对于 s u b t a s k 2 subtask2 ,搜索一下,map或者hash判重
对于 s u b t a s k 3 subtask3 ,我们分析一下限制条件,写成不等式得到:
( d 1 , f 1 ) , ( d 2 , f 2 ) ( d : , f : ) 对于两次攻击(d1,f1),(d2,f2) (d:天数,f:嘲讽力)
1. c [ i ] f 1 + f 2 1.c[i]\geq f1+f2 (不能把血打成负数)
2. D d 1 d 2 c [ i ] f 1 + f 2 D 2.D-d1-d2 \geq c[i]-f1+f2 (要打死,剩下的要通过顶嘴打掉,D为可自由行动的天数)

把二式移项,令D单独在左边,枚举 d 1 f 1 d1-f1 ,则如果 f f 为有序,那么根据一式便满足单调性,要求 d 2 f 2 d2-f2 的最小值就很好求了
特殊情况:如果可以直接嘴炮打死,就直接输出1
Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=105,INF=990000000;
struct fxxk{int day,F,L;};
map<int,int>mp[N];
pair<int,int>s[2000005];
int n,m,f[N][N],w[N],a[N],mc,D=0,c[N],lim,cnt=0;
inline void dp(){
	memset(f,128,sizeof(f));f[0][mc]=0;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=mc-a[i];j++){
			f[i][min(mc,j+w[i])]=max(f[i][min(mc,j+w[i])],f[i-1][j+a[i]]);
			f[i][j]=max(f[i][j],f[i-1][j+a[i]]+1);
			D=max(D,f[i][j]);
		}
}
inline void bfs(int tot){
	queue<fxxk>q;q.push((fxxk){1,1,0});
	while(!q.empty()){
		fxxk x=q.front();q.pop();
		if(x.day<tot){
			q.push((fxxk){x.day+1,x.F,x.L+1});
			if(x.L>1 && x.F<lim/x.L && !mp[x.L][x.L*x.F]){
				fxxk y=(fxxk){x.day+1,x.F*x.L,x.L};
				mp[x.L][x.L*x.F]=1;
				s[++cnt]=make_pair(y.F,y.day);q.push(y);
			}
		}
	}
}
int main(){
	n=read();m=read();mc=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<=m;i++) c[i]=read(),lim=max(lim,c[i]);
	dp();
	bfs(D);sort(s+1,s+cnt+1);
	for(int i=1;i<=m;i++){
		int ans=0,minn=INF;
		if(c[i]<D){puts("1");continue;}
		for(int j=cnt,k=1;j;j--){
			while(k<=cnt && s[k].first+s[j].first<=c[i]) minn=min(minn,s[k].second-s[k].first),k++;
			if(minn+c[i]-s[j].first+s[j].second<=D){ans=1;break;}
			if(s[j].first<=c[i] && s[j].second+c[i]-s[j].first<=D) {ans=1;break;}
		}
		cout<<ans<<"\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43346903/article/details/88957931
今日推荐