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,你能且仅能选择如下的行为之一:

  1. 还一句嘴,大佬会有点惊讶,导致大佬的自信值 C 减小 1。

  2. 做一天的水题,使得自己的当前自信值增加 w[i], 并将新自信值和自信值上限 mc 比较,若新自信值大于 mc,则新自信值更新为 mc。例如, mc=50, 当前自信值为 40, 若w[i]=5,则新自信值为 45,若 w[i]=11,则新自信值为 50。

  3. 让自己的等级值 L 加 1。

  4. 让自己的讽刺能力 F 乘以自己当前等级 L,使讽刺能力 F 更新为 F*L。

  5. 怼大佬,让大佬的自信值 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;

 

 

 

posted @ 2018-03-25 19:10  大米饼  阅读(379)  评论(0编辑  收藏  举报