环境里有 n 个怪物,他们的生命值用一个正整数表示。现在,你可以使用两种魔法,对怪物进行攻击。当怪物的生命值小于等于 0 时,他便被消灭了。
魔法箭,对摸个生物造成 k 点伤害,对一个生物最多使用一次,但没有使用次数限制。
亵渎,对所有生物造成一点伤害,如果杀死了某个生物,则继续自动重新使用该法术。只能主动使用一次,且必须最后使用。
请问,最多能消灭多少个怪物?亵渎法术最多能释放几次?
输入格式
第一行两个整数 n 和 k ,表示怪物的数量和法术的伤害。第二行 n 个正整数,依次表示每个怪物的生命值。
输出格式
一行,两个整数,表示最多能消灭多少怪物和亵渎法术最多被释放的次数。
数据范围
对于 40% 的数据 n≤200 。
对于全部数据, n,k≤100000, 怪物的生命上限为 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;
}