题解 P3724 【[AH/HNOI2017]大佬】
DOTime
2017-12-02 09:53:34
标签:DP+map(Hash)+单调性
题解:
觉得这道题很妙,题目说每天每天有那么多选择,但是其实只要不死,那么选择最大伤害可以枚举,而不死又可以DP。
具体来说:首先要保证不死,那么我们设dp[i][j]代表在前i天,第i天的自信值为j,那么前i天中可以最多不刷题几天,也就是,可以用来打伤害。其实就相当于全部的状态都枚举出来的裸DP。
做完这一步,我们取dp数组的最大值,也就是我最多有多少天可以使用。假设D天。
在D天中,要选择3、4、5即可,剩下的天数,多退少补,执行1操作到正好即可。
设两次怼大佬的情况分别为(d1,f1)、(d2,f2),即花费多少天打出多少伤害。假设大佬生命为HP,那么:
HP-f1-f2>=0,否则大佬生命值就为负了,然后还要满足能打死,也就是HP-f1-f2<=D-d1-d2,也就是大佬剩下的生命我要能在剩余天数内执行1操作打完。
当然这是怼两次的情况,怼一次就是HP-f1>=0 && HP-f1<=D-d1即可,不怼就是 HP>=0 && HP<=D即可。只要有符合条件的就是1.
我们bfs+判重的枚举出所有可能的(d,f),然后以f为第一关键字,d为第二关键字排序。按照f从大到小的枚举第一次怼,然后发现这个是有单调性的:我们移项D>=HP-f1+d1-f2+d2,f1,d1是枚举的,那么自然-f1+d2最小即可。那么在满足f1+f2<=HP的条件下,增大f2,即指针向后扫,每次取-f1+d2最小,然后比较,O(状态数)扫过去即可。
```cpp
http://www.cnblogs.com/D-O-Time/p/7953435.html
1 #include<queue>
2 #include<cstdio>
3 #include<cstring>
4 #include<iostream>
5 #include<algorithm>
6 #define LL long long
7 #define pa pair<int,int>
8 using namespace std;
9 const int MAXN=2100000,mod=3587201;
10 pa Q[MAXN];
11 int n,m,mc,D,MAXC,tp,Maxsize;
12 int dp[105][105],a[105],w[105],C[105];
13 struct ed {int step,F,L;};
14 queue<ed>Que;
15 struct ED
16 {
17 struct gg{int x,y,Next;}edge[MAXN];
18 int top,head[mod+5];
19 void insert(int x,int y)
20 {
21 int tmp=((LL)x*100+y)%mod;
22 edge[++top]=(gg){x,y,head[tmp]};head[tmp]=top;
23 }
24 bool query(int x,int y)
25 {
26 int tmp=((LL)x*100+y)%mod;
27 for(int i=head[tmp];i;i=edge[i].Next)
28 if(x==edge[i].x&&y==edge[i].y)return 1;
29 return 0;
30 }
31 }map;//手写map,Hash判重
32 inline int gi() { int res; scanf("%d",&res); return res; }
33 void prepare()
34 {
35 for(int i=1;i<=n;i++)
36 for(int j=a[i];j<=mc;j++)
37 {
38 dp[i][j-a[i]]=max(dp[i][j-a[i]],dp[i-1][j]+1);
39 int tmp=min(mc,j-a[i]+w[i]);
40 dp[i][tmp]=max(dp[i][tmp],dp[i-1][j]);
41 }
42 for(int i=1;i<=n;i++)
43 for(int j=0;j<=mc;j++)
44 D=max(D,dp[i][j]);
45 }
46 void bfs()
47 {
48 Que.push((ed){1,1,0});//初始状态:使用一天,打出1伤害,等级0。
49 while(!Que.empty())
50 {
51 ed now=Que.front();
52 Que.pop();
53 if(now.step<D)
54 {
55 Que.push((ed){now.step+1,now.F,now.L+1});//加1等级
56 if(now.L>1 && (LL)now.F*now.L<=(LL)MAXC && !map.query(now.F*now.L,now.step+1))//注意爆int,hash炸掉了也没事。
57 {
58 ed tmp=(ed){now.step+1,now.F*now.L,now.L};
59 Que.push(tmp);
60 Q[++tp]=(pa){tmp.F,tmp.step};//此处才加入队列,不能直接从Que取出来就加入,因为只加等级的状态是不优的,只有乘了,才更好。
61 map.insert(tmp.F,tmp.step);
62 }
63 }
64 }
65 }
66 int main()
67 {
68 freopen("dalao.in","r",stdin);
69 freopen("dalao.out","w",stdout);
70 n=gi();m=gi();mc=gi();
71 for(int i=1;i<=n;i++)a[i]=gi();
72 for(int i=1;i<=n;i++)w[i]=gi();
73 for(int i=1;i<=m;i++)C[i]=gi(),MAXC=max(MAXC,C[i]);
74 prepare(); bfs();
75 sort(Q+1,Q+tp+1);
76 for(int i=1;i<=m;i++)
77 {
78 if(C[i]<=D) {puts("1"); continue;}
79 int flag=0,mi=0x3f3f3f3f;
80 for(int j=tp,k=1;j>=1;j--)//按照单调性找是否有解。
81 {
82 while(k<tp && Q[k].first+Q[j].first<=C[i]) mi=min(mi,Q[k].second-Q[k].first) , k++;
83 if(mi+Q[j].second-Q[j].first+C[i]<=D) {flag=1; break;}
84 if(Q[j].first<=C[i] && C[i]-Q[j].first+Q[j].second<=D) {flag=1; break;}
85 }
86 printf("%d\n",flag);
87 }
88 return 0;
89 }
```