[HNOI2017]大佬

Posted D O Time

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HNOI2017]大佬相关的知识,希望对你有一定的参考价值。

标签: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(状态数)扫过去即可。

 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 }

以上是关于[HNOI2017]大佬的主要内容,如果未能解决你的问题,请参考以下文章

[AH/HNOI2017]大佬

bzoj4828 [Hnoi2017]大佬

HNOI2017大佬

[HNOI2017]大佬

#10 [AH2017/HNOI2017]大佬

HNOI2017 滚粗记