圖論 - 教科書般的褻瀆 - from jisuanke

zhuo1ang發表於2018-04-05


環境裡有 n 個怪物,他們的生命值用一個正整數表示。現在,你可以使用兩種魔法,對怪物進行攻擊。當怪物的生命值小於等於 0 時,他便被消滅了。

  1. 魔法箭,對摸個生物造成 k 點傷害,對一個生物最多使用一次,但沒有使用次數限制。

  2. 褻瀆,對所有生物造成一點傷害,如果殺死了某個生物,則繼續自動重新使用該法術。只能主動使用一次,且必須最後使用。

請問,最多能消滅多少個怪物?褻瀆法術最多能釋放幾次?

輸入格式

第一行兩個整數 n 和 k ,表示怪物的數量和法術的傷害。第二行 n 個正整數,依次表示每個怪物的生命值。

輸出格式

一行,兩個整數,表示最多能消滅多少怪物和褻瀆法術最多被釋放的次數。

資料範圍

對於 40% 的資料 n200 。

對於全部資料, n,k100000, 怪物的生命上限為 100000

樣例輸入

5 1
1 2 3 5 7

樣例輸出

4 5

建圖: 把血量看作點,對於 ai > k,連線 ai 和 ai - k

這樣,圖將會分成若干個聯通塊,分別考察每一個聯通塊:

    1:這個聯通塊具有不少於節點數量的邊數,則這些節點代表血量都可以取到;

    2:這個聯通是樹,還有兩種情況:

             (1):聯通塊中的點,在給定序列中出現次數超過聯通塊中邊數(這種情況是由於此聯通塊中最小點無法向更低血量連邊而出現的),這些節點代表的血量同樣都可以取到;

             (2): otherwise,序列出現次數 = 邊數,這個聯通塊有一個點會無法取到,簡單的貪心策略,不取塊中的最大血量。


綜合以上的判斷方式,我們可以得知最佳處理下會得到哪些血量,進而求得了褻瀆次數。

同時,可以證明,採取這種操作可以殺死最多的怪物。


Code

#include <iostream>
#include <vector>

using namespace std;

#define REP(i, a, b) for (int i = (a), i##_end_ = (b); i < i##_end_; ++i)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define mp make_pair
#define x first
#define y second
#define pb push_back
#define SZ(x) (int((x).size()))
#define ALL(x) (x).begin(), (x).end()

#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)

typedef long long LL;

const int N = 120000;
int n, k, cnt, ans1, ans2;
int a[N], vis[N], vtx[N], egs[N], mxn[N];
bool tree[N], ok[N];
vector< vector<int> > edges(N);

void dfs(int cur, int fa, int tag) {
	vis[cur] = tag;
	REP(i, 0, edges[cur].size()) {
		int vertex = edges[cur][i];
		if (vis[vertex] && vertex != fa) tree[tag] = false;
		if (!vis[vertex]) dfs(vertex, cur, tag);
	}
}

int main() {
	memset(tree, 1, sizeof tree);
	scanf("%d%d", &n, &k);
	REP(i, 1, n + 1) scanf("%d", &a[i]);
	REP(i, 1, n + 1)
		if (a[i] > k)
			edges[a[i]].pb(a[i] - k),
			edges[a[i] - k].pb(a[i]);
	REP(i, 1, N) if (!vis[i]) dfs(i, 0, ++cnt);
	REP(i, 1, n + 1) vtx[vis[a[i]]]++;
	REP(i, 1, n + 1) if (a[i] > k) egs[vis[a[i]]]++;
	REP(i, 1, N) mxn[vis[i]] = max(mxn[vis[i]], i);
	REP(i, 1, N)
		if (vtx[vis[i]] > egs[vis[i]]) ok[i] = true;
		else if (tree[vis[i]] && i == mxn[vis[i]]) ok[i] = false;
		else ok[i] = true;
	REP(i, 1, N) if (ok[i] == false) { ans2 = i - 1; break; }
	REP(i, 1, n + 1) if (a[i] - k <= ans2) ans1++;
	cout << ans1 << " " << ans2 + 1 << endl;
	return 0;
}

相關文章