bzoj4828 hnoi2017 大佬
题目描述
人们总是难免会碰到大佬。他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语。 你作为一个 OIER,面对这样的事情非常不开心,于是发表了对大佬不敬的言论。 大佬便对你开始了报复,你也不示弱,扬言要打倒大佬。
现在给你讲解一下什么是大佬,大佬除了是神犇以外,还有着强大的自信心,自信程度可以被量化为一个正整数 C( 1<=C<=10^8), 想要打倒一个大佬的唯一方法是摧毁 Ta 的自信心,也就是让大佬的自信值等于 0(恰好等于 0,不能小于 0)。 由于你被大佬盯上了,所以你需要准备好 n(1<=n<=100)天来和大佬较量,因为这 n 天大佬只会嘲讽你动摇你的自信,到了第n+1 天,如果大佬发现你还不服,就会直接虐到你服,这样你就丧失斗争的能力了。
你的自信程度同样也可以被量化,我们用 mc (1 <= mc <= 100)来表示你的自信值上限。
在第 i 天( i>=1),大佬会对你发动一次嘲讽,使你的自信值减小 a[i],如果这个时刻你的自信值小于 0 了,那么你就丧失斗争能力,也就失败了(特别注意你的自信值为 0 的时候还可以继续和大佬斗争)。 在这一天, 大佬对你发动嘲讽之后,如果你的自信值仍大于等于 0,你能且仅能选择如下的行为之一:
-
还一句嘴,大佬会有点惊讶,导致大佬的自信值 C 减小 1。
-
做一天的水题,使得自己的当前自信值增加 w[i], 并将新自信值和自信值上限 mc 比较,若新自信值大于 mc,则新自信值更新为 mc。例如, mc=50, 当前自信值为 40, 若w[i]=5,则新自信值为 45,若 w[i]=11,则新自信值为 50。
-
让自己的等级值 L 加 1。
-
让自己的讽刺能力 F 乘以自己当前等级 L,使讽刺能力 F 更新为 F*L。
- 怼大佬,让大佬的自信值 C 减小 F。并在怼完大佬之后,你自己的等级 L 自动降为 0,讽刺能力 F 降为 1。由于怼大佬比较掉人品,所以这个操作只能做不超过 2 次。
特别注意的是,在任何时候,你不能让大佬的自信值为负,因为自信值为负,对大佬来说意味着屈辱,而大佬但凡遇到屈辱就会进化为更厉害的大佬直接虐飞你。在第 1 天,在你被攻击之前,你的自信是满的(初始自信值等于自信值上限 mc), 你的讽刺能力 F 是 1, 等级是 0。
现在由于你得罪了大佬,你需要准备和大佬正面杠,你知道世界上一共有 m( 1<=m<= 20)个大佬,他们的嘲讽时间都是 n 天,而且第 i 天的嘲讽值都是 a[i]。不管和哪个大佬较量,你在第 i 天做水题的自信回涨都是 w[i]。 这 m 个大佬中只会有一个来和你较量( n 天里都是这个大佬和你较量),但是作为你,你需要知道对于任意一个大佬,你是否能摧毁他的自信,也就是让他的自信值恰好等于 0。和某一个大佬较量时,其他大佬不会插手。
输入输出格式
输入格式:
第一行三个正整数 n,m,mc。分别表示有 n 天和 m 个大佬, 你的自信上限为 mc。
接下来一行是用空格隔开的 n 个数,其中第 i(1<=i<=n)个表示 a[i]。
接下来一行是用空格隔开的 n 个数,其中第 i(1<=i<=n)个表示 w[i]。
接下来 m 行,每行一个正整数,其中第 k(1<=k<=m)行的正整数 C[k]表示第 k 个大佬的初始自信值。
输出格式:
共 m 行,如果能战胜第 k 个大佬(让他的自信值恰好等于 0),那么第 k 行输出 1,否则输出 0。
题意:
一个大佬自信值为c,你的自信值为mc,大佬每天会减少你的自信值a[i],你可以选择刷水题让自己的自信值提高w[i],或者让c--,或者让自己等级L++,或者给使讽刺能力F*=L,或者怼大佬造成F的伤害(mc-=F),然后初始化L,F(L=0,F=1)。但是怼大佬只能小于等于两次,多个询问c,问能不能恰好归零c。
题解:
①量太多,直接dp,掰了掰手指后发现:会大事不妙;
②水题刷多了不好,先预处理最大化不刷水题的天数,设计dpij表示第i天自信值为j时有多少天可以进行除刷水题以外的操作。dpij 有两种转移 不做刷和刷:即dpi-1,j+a[i] –> dpi,j 和 dpi-1,j+a[i] –> dpi,min(j+w[i],m); (不要小看这个dp,大米兔认为写的话还是要理解得很清楚)
③现在我们可以在被大佬打压之余自由地安排剩下的人生:
进行其余的操作,在经过一些神秘YY之后,如果利用哈希表判断一下F,L不重复,状态不多,可以暴力bfs出(d,f) 为用d天怼一次大佬可以造成恰好f的伤害,并且由于我们有水题神器,d天可以有f,d+1天就不需要f了,相当于您悠闲地再做一天水题就好了;
③下面我们对大佬打出精确的伤害:
分类讨论,在剩下的D天里,可以做两件事:让c-1,让c-某个f;
不做f C[i] <= D;
一次f C – (D-d) <= f <= C d<=D;
两次f C – (D-d1-d2) <= f1 + f2 <= C d1+d2<=D;
(既不能把大佬XX成负数同时保证能XX完)
零次和一次直接判断,两次的时候按f排序,f1变小,使f2变大并满足右边,维护左边的最大值,这样就保证扫一次就好了(另外不需要特判d1+d2<=D,因为反之式子一定不成立)!
下面的代码记录了一个蒟蒻花了n天勇敢战胜m个大佬的故事:
1 #include<cstdio> 2 #include<istream> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #define N 210 7 #define M 2000100 8 #define inf 0x3f3f3f3f 9 #define ull unsigned long long 10 using namespace std; 11 typedef pair<int,int>pii; 12 int n,m,mc,C[N],a[N],w[N],D,f[N][N],mxC; 13 pii q[M];int top; 14 struct node{int f,d,l;}; 15 queue<node> Q; 16 const int sz = 2*1e7; 17 struct Hash{ 18 struct Edge{int x,y,nt;ull w;}E[sz+1];int hd[sz+1],o; 19 void ins(ull x,ull y){ 20 ull w = (x * 10101 + y * 101); 21 E[++o] = (Edge){x,y,hd[w%sz],w}; 22 hd[w%sz] = o; 23 } 24 int query(ull x,ull y){ 25 ull w = (x * 10101 + y * 101); 26 for(int i = hd[w%sz];i;i = E[i].nt) 27 if(E[i].x==x&&E[i].y==y) return 1; 28 return 0; 29 } 30 }H; 31 char gc(){ 32 static char *p1,*p2,s[1000000]; 33 if(p1==p2) p2=(p1=s)+fread(s,1,1000000,stdin); 34 return(p1==p2)?EOF:*p1++; 35 } 36 int rd(){ 37 int x = 0; char c = gc(); 38 while(c<'0'||c>'9') c = gc(); 39 while(c>='0'&&c<='9') x = x * 10 + c - '0',c = gc(); 40 return x; 41 } 42 void bfs(){ 43 Q.push((node){1,1,0}); 44 //q[++top] = make_pair(1,1); 45 H.ins(1,0); 46 while(!Q.empty()){ 47 node x = Q.front(); Q.pop(); 48 if(x.d>=D) continue; 49 Q.push((node){x.f,x.d+1,x.l+1}); 50 if(x.l > 1 && x.f <= mxC/x.l && !H.query(x.l*x.f,x.l)){ 51 node tmp = (node){x.l*x.f,x.d+1,x.l}; 52 H.ins(tmp.f,tmp.l); 53 q[++top] = make_pair(tmp.f,tmp.d); 54 Q.push(tmp); 55 } 56 } 57 } 58 int main() 59 { //freopen("bzoj4828.in","r",stdin); 60 //freopen("bzoj4828.out","w",stdout); 61 n = rd(); m = rd(); mc = rd(); 62 for(int i = 1;i <= n;i++) a[i] = rd(); 63 for(int i = 1;i <= n;i++) w[i] = rd(); 64 for(int i = 1;i <= m;i++) mxC = max(mxC,C[i] = rd()); 65 memset(f,128,sizeof(f));f[0][mc] = 0; 66 for(int i = 1;i <= n;i++){ 67 for(int j = 0;j <= mc - a[i];j++) 68 f[i][j] = max(f[i][j],f[i - 1][j + a[i]] + 1), 69 f[i][min(mc,j+w[i])] = max(f[i][min(mc,j+w[i])],f[i-1][j+a[i]]); 70 for(int j = 0;j <= mc;j++) D =max(f[i][j],D); 71 } 72 bfs(); 73 sort(q+1,q+top+1); 74 for(int i = 1;i <= m;i++){ 75 int mx = -inf,ans = 0; 76 if(C[i]<=D) {puts("1"); continue;} 77 for(int j = top,k = 0;j >= 1;j--){ 78 while(k<top && q[k+1].first + q[j].first <= C[i]) k++,mx = max(mx,q[k].first - q[k].second); 79 if(q[j].first <= C[i] && q[j].first + D - q[j].second >= C[i]) {ans = 1; break;} 80 if(q[j].first - q[j].second + mx + D >= C[i]) {ans = 1; break;} 81 } 82 printf("%d\n",ans); 83 } 84 return 0; 85 }//by tkys_Austin;