• HNOI 2017 怼大佬


    题意

    (m)个大佬,第(i)个大佬的自信值为(C_i)。每次怼大佬之前,你的自信值为(mc),等级(L=0),嘲讽值 (F=1) 。对于每一个大佬,都有(n)天时间,第(i)天你将会被大佬打击,自信值下降(a_i)。如果在某一天你的自信值为负数,那么你失败了

    每一天你可以进行以下操作的任意一个:

    1. 还一句嘴,使大佬自信值下降(1)

    2. 刷水题,使自己的自信值增加(w_i)

    3. 把自己的等级(+1)

    4. 把自己的(F)乘上(L)

    5. 怼大佬,使得大佬的自信值下降 (F),之后 (L = 0,F = 1)。这个操作可以进行 (k={0,1,2})

    你必须在某一天使大佬的自信值恰好为 (0) 。如果大佬的自信值为负,则判定你失败

    对于每个大佬,请判断你是否成功


    解法

    可以发现胜利的必要条件是自己能在大佬被怼死之前存活下来

    我们设一个DP (f[i][j]) 为在第 (i) 天,剩余自信值为 (j) 时最多能够把多少天用来怼大佬

    在该数组中取一个最大值即可求出最多能有多少天用于怼大佬,把这个天数设置为 (D)

    可以发现怼大佬这一操作的威力只与操作 (4,5) 有关

    一遍 BFS 求出所有的二元组 (<D, F>) 代表花 (D) 天可以对大佬造成的伤害 (F)

    贪心的思考,对于某一个 (F) ,我们只需要保留最小的 (D) 即可,这样可以减少很多状态

    你能怼死大佬有三种情况:

    1. 不怼大佬,只还嘴。满足 (C_i leq D) 即可

    2. 只怼一次大佬。这时能怼死大佬需要满足存在一个二元组 ((d',f'))使得 (f'leq C_i) 并且 (f'+D-d' ge C_i)。(即怼不死的部分用还嘴耗掉)

    3. 怼两次大佬。可以发现,当 (f_1 +f_2 leq C_i) 并且 (f_1+f_2+(D-d_1-d_2)geq C_i) 时可以将大佬怼死。我们把所有二元组按 (f) 排序,第一个条件用 two_pointers 维护(对于某个 (f) ,能与它配对的二元组是一个连续的区间),至于第二个,我们把第二个指针视作必选的二元组,那么只需要在它可以配对的区间中选一个 (f-d) 最大的即可


    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MAX_N = 210;
    const int MAX_S = 1e6 + 10;
    
    #define Pii pair<int, int>
    #define mp make_pair
    #define x first
    #define y second
    
    int read();
    
    int N, Q, Hp, K;
    int T, mxc, top; 
    
    int b[MAX_N], h[MAX_N], p[MAX_N];
    int f[MAX_N][MAX_N * 10];
    
    struct sta { int i, F, L; };
    
    queue<sta> que;
    Pii a[6 * MAX_S], c[MAX_S];
    
    struct Hash_Table {
    #define mod 4194303	
    	
    	int cap;
    	int head[mod + 10];
    	int nxt[mod + 10], a[mod + 10], b[mod + 10];
    	
    	Hash_Table() : cap(0) {}
    	
    	int Hash(Pii pr) {
    		int x = pr.x, y = pr.y;
    		return (1LL * x * 20030207 + y) & mod;
    	}
    	void insert(Pii pr) {
    		int u = Hash(pr);
    		nxt[++cap] = head[u];
    		head[u] = cap;
    		a[cap] = pr.x, b[cap] = pr.y;
    	}
    	int count(Pii pr) {
    		int u = Hash(pr);
    		for (int i = head[u]; i; i = nxt[i])
    			if (a[i] == pr.x && b[i] == pr.y)  return 1;
    		return 0;
    	}
    #undef mod
    } Map;
    
    inline void chkmax(int& x, int y) { x = x > y ? x : y; }
    
    void init() {
    	for (int i = 1; i <= N; ++i)
    		for (int j = b[i]; j <= Hp; ++j) {
    			chkmax(f[i][j - b[i]], f[i - 1][j] + 1),
    			chkmax(f[i][min(j - b[i] + h[i], Hp)], f[i - 1][j]);
    		}
    	for (int i = 1; i <= N; ++i)
    		for (int j = 0; j <= Hp; ++j)  chkmax(T, f[i][j]);
    } 
    
    void BFS() {
    	que.push((sta){1, 1, 0});	
    	while (!que.empty()) {
    		sta fr = que.front(); que.pop();
    		int I = fr.i, F = fr.F, L = fr.L;
    		if (I >= T)  continue;
    		que.push((sta){I + 1, F, L + 1});
    		if (L > 1 && 1LL * L * F <= mxc && !Map.count(mp(1LL * L * F, I + 1))) {
    			a[++top] = mp(L * F, I + 1);
    			que.push((sta){I + 1, L * F, L});
    			Map.insert(a[top]);
    		}
    	}
    	int tp = 0;
    	sort(a + 1, a + top + 1);
    	for (int i = 1; i <= top; ++i) 
    		if (a[i].x ^ a[i - 1].x)  c[++tp] = a[i];
    	top = tp;
    }
    
    int main() {
    	
    	N = read(), Hp = read(), K = read();
    	for (int i = 1; i <= N; ++i)  b[i] = read();
    	for (int i = 1; i <= N; ++i)  h[i] = read();
    	
    	Q = read();
    	for (int i = 1; i <= Q; ++i)  p[i] = read();
    	for (int i = 1; i <= Q; ++i)  chkmax(mxc, p[i]);
    	
    	init();
    	BFS(); 
    	
    	for (int i = 1; i <= Q; ++i) {
    		if (!K) {
    			puts(T >= p[i] ? "Yes" : "No"); 
    			continue;
    		} else if (K == 1) {
    			if (T >= p[i]) { puts("Yes"); continue; }
    			int fl = 0;
    			for (int j = 1; j <= top; ++j) {
    				if (c[j].x > p[i])  break;
    				if (T - c[j].y + c[j].x >= p[i]) { fl = 1; break; }
    			}
    			puts(fl ? "Yes" : "No");
    		} else {
    			if (T >= p[i]) { puts("Yes"); continue; }
    			int mn = (int)1e9, l = 1, fl = 0;
    			for (int j = top; j >= 1; --j) {
    				while (l < j && c[l].x + c[j].x <= p[i])  mn = min(mn, c[l].y - c[l].x), ++l;
    				if (mn + c[j].y - c[j].x + p[i] <= T)  { fl = 1; break; }
    				if (c[j].x <= p[i] && T - c[j].y + c[j].x >= p[i]) { fl = 1; break; }
    			}
    			puts(fl ? "Yes" : "No");
    		}
    	}
    	
    	return 0;
    }
    
    int read() {
    	int x = 0, c = getchar();
    	while (!isdigit(c))  c = getchar();
    	while (isdigit(c))   x = x * 10 + c - 48, c = getchar();
    	return x;	
    }
    
  • 相关阅读:
    document.load 和document.ready 两个事件的区别
    undefined和null的区别
    点击返回上一个界面事件
    APP上点击事件时 取消点击的阴影
    MUI控制安卓手机自带的返回键 禁止返回
    css过滤镜实现颜色渐变
    CSS3 Media Queries模板:max-width和min-width
    什么是响应式网页设计?
    实用的CSS3-渐变背景色
    Oracle EBS 报表日期格式问题
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11680057.html
Copyright © 2020-2023  润新知