1. 程式人生 > >【題解】HNOI2017大佬

【題解】HNOI2017大佬

spa using getch max read UC AD 中一 continue

  哎……做了幾個小時最後還是沒能想到懟大佬的合法性到底怎麽搞。寫暴力爆搜感覺復雜度爆炸就沒敢寫 bfs / dfs 一類,後來發現在種種的約束條件下(遠小於所給的 \(n, m\))復雜度完全是可以承受的。不過就算想到了這一步諒我也想不出用單調棧來搞兩次的組合吧。

  這題最開始就應該發現:扣血和回血完全是可以分開的兩個操作。為什麽這個點很容易發現呢:1.扣血的多少與時間是無關的。2.本題要求活著(血量 >= 0)而非最大化血量。所以第一步顯然應當 dp 出在保證活著的前提下最多能夠空出多少天來對付 ‘大佬’。

  然後來考慮,假設我們最多擁有 \(D\) 天來攻擊,是否存在一種攻擊方案使得‘大佬‘的血量正好為0。預處理出 \(g[k][d]\) 代表 \(d\) 次操作,是否可能對‘大佬‘產生 \(k\) 點傷害。(這裏用Map來存儲,並註意這裏的方案只考慮懟一次)。然後我們假設存在一種合法的方案懟大佬兩次使得大佬血量正好為0,第一次扣血 \(f1\) 點,耗費時間為 \(d1\) 天,第二次扣血 \(f2\) 點,耗費時間為 \(d2\) 天。他們之間該滿足什麽樣的關系式呢? (設大佬血量為 \(M\))

\(M >= f1 + f2\)

\(M - f1 - f2 <= D - d1 - d2\)

  其中(1)式表示不能減為負數,(2)式表示沒有減完的血量可以通過還嘴來減掉。之後將(2)式移項(構造常量與非常量之間的不等式以方便檢查)

\(D >= M - f1 - f2 + d1 + d2\)

  然後我們選擇枚舉這兩次懟大佬之間的其中一個,則不確定的項即為 \(d2 - f2\) ,因為要求右邊的小,所以求這個的最小值之後判斷就好了。又因為有之前(1)式的約束,我們在固定了 \(f1\) 後如果能夠將 \(f\) 從小到大排序,即可方便判斷何時一定不滿足(單調遞增)。

#include <bits/stdc++.h>
using namespace std;
#define maxn 110
#define ll long long
#define INF 990000000
int n, m, mc, a[maxn], w[maxn];
int ans, maxx, top, B[maxn];
int mark[maxn], f[maxn][maxn];
pair <int, int> S[2000010];
map <int, int> Map[maxn];

struct node
{
    int num, v, lev;
};

int
read() { int x = 0, k = 1; char c; c = getchar(); while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); } while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); return x * k; } void upmax(int &x, int y) { x = x > y ? x : y; } void DP() { for(int i = 1; i <= n; i ++) for(int j = 0; j <= mc - a[i]; j ++) { upmax(f[i][min(mc, j + w[i])], f[i - 1][j + a[i]]); // add blood upmax(f[i][j], f[i - 1][j + a[i]] + 1); // do nothing ans = max(f[i][j], ans); } } void BFS(int tot) { queue <node> q; q.push((node) {1, 1, 0}); while(!q.empty()) { node x = q.front(); q.pop(); if(x.num < tot) { q.push((node) {x.num + 1, x.v, x.lev + 1}); if(x.lev > 1 && x.v < maxx / x.lev && !Map[x.lev][x.v * x.lev]) { node y = (node) { x.num + 1, x.v * x.lev, x.lev}; Map[x.lev][x.v * x.lev] = 1; S[++ top] = make_pair(y.v, y.num); q.push(y); } } } } int main() { n = read(), m = read(), mc = read(); for(int i = 1; i <= n; i ++) a[i] = read(); for(int i = 1; i <= n; i ++) w[i] = read(); for(int i = 1; i <= m; i ++) B[i] = read(), maxx = max(maxx, B[i]); memset(f, 128, sizeof(f)); f[0][mc] = 0; DP(); BFS(ans); sort(S + 1, S + top + 1); for(int i = 1; i <= m; i ++) { int t = B[i], fans = 0, fmin = INF; if(t < ans) { printf("1\n"); continue; } for(int j = top, k = 1; j; j --) { while(k <= top && S[k].first + S[j].first <= t) fmin = min(fmin, S[k].second - S[k].first), k ++; if(fmin + t - S[j].first + S[j].second <= ans) { fans = 1; break; } if(S[j].first <= t && S[j].second + t - S[j].first <= ans) { fans = 1; break; } } printf("%d\n", fans); } return 0; }

【題解】HNOI2017大佬