题解 P3724 【[AH/HNOI2017]大佬】

DOTime

2017-12-02 09:53:34

Solution

标签: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 } ```