首先對於每一個敵人單獨預處理時間線(即在何時能夠殺死這個敵人,何時殺不死了),而後經過一個總時間線去更新答案。c++
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; map<long long, int> add, erase; set<long long> timeline; vector<pair<long long, int> > V[N]; int max_health[N], regen[N]; int main() { int n, m, bounty, increase, damage; cin >> n >> m >> bounty >> increase >> damage; for (int i = 0; i < n; i++) { int h; scanf("%d%d%d", &max_health[i], &h, ®en[i]); V[i].push_back(make_pair(0, h)); } for (int i = 0; i < m; i++) { int t, id, h; scanf("%d%d%d", &t, &id, &h); V[id - 1].push_back(make_pair(t, h)); } for (int i = 0; i < n; i++) { auto& v = V[i]; sort(v.begin(), v.end()); if(increase > 0) { if(damage >= max_health[i] || (!regen[i] && v.back().second <= damage)) { return puts("-1") * 0; } } for (int j = 0; j < v.size(); j++) { if(v[j].second > damage) continue; add[v[j].first]++; long long interval = 2e9; if(j != v.size() - 1) interval = min(interval, v[j + 1].first - v[j].first - 1); if(regen[i]) interval = min(interval, 1LL * (damage - v[j].second) / regen[i]); erase[v[j].first + interval]++; timeline.insert(v[j].first); timeline.insert(v[j].first + interval); } } long long ans = 0, cnt = 0; for (long long x : timeline) { cnt += add[x]; ans = max(ans, cnt * (bounty + x * increase)); cnt -= erase[x]; } cout << ans << endl; return 0; }
雖然 \(n\) 和 \(m\) 的數據都很大( \(n*m\)的矩陣),可是 \(k\) 很小,那咱們就應該考慮是否可能要枚舉 \(k\) 了。佈局
求指望分爲求分子和分母。分母是全部的撒網方案數,分母沒法改變,咱們要求分子儘量大。spa
分子其實是要求在某種魚的佈局狀況下,全部可能的撒網方案下,魚出現的次數之和最大。那麼能夠考慮一條魚在不一樣位置對答案的貢獻最大是多少,也就是說有幾種方案的網能夠罩住這條魚所在的位置,這裏要對行列都預處理下,再用優先隊列維護下答案,由於 \(n\) 和 \(m\) 都很大咱們沒法所有枚舉出來,最後取最大的 \(k\) 個便可。code
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; int a[N], b[N]; struct P { long long x; int i; P() {} P(long long x, int i): x(x), i(i) {} friend bool operator <(P a, P b) { return a.x < b.x; } }; priority_queue<P> q; int main() { int n, m, r, k; cin >> n >> m >> r >> k; for (int i = 1; i <= n; i++) { a[i] = min(n - r + 1, i) - max(i - r + 1, 1) + 1; } for (int i = 1; i <= m; i++) { b[i] = min(m - r + 1, i) - max(i - r + 1, 1) + 1; } sort(a + 1, a + n + 1); sort(b + 1, b + m + 1); for (int i = 1; i <= n; i++) { q.push(P(a[i] * b[m], m)); } long long z = 1LL * (n - r + 1) * (m - r + 1); long long s = 0; while(k--) { P p = q.top(); q.pop(); s += p.x; if(p.i > 1) { p.x /= b[p.i]; p.i--; p.x *= b[p.i]; q.push(p); } } printf("%.10f\n", 1.0 * s / z); return 0; }
二分答案 \(ans\),問題轉化成小於等於 \(ans\) 的有多少個數。排序
dfs 暴力枚舉素因子構造數,固然不能直接枚舉全部素因子,考慮分紅兩組(按下標奇偶交替分組),使得後面構造出的兩組數數量儘量相等。假設數量最多爲 \(n\),那麼將兩組排序後,\(O(n)\) 的複雜度就能夠回答轉化後的問題,固然排序會有 \(O(nlogn)\) 的複雜度。隊列
由這道題可見 CF 的速度仍是很快的,本地跑 3 秒,CF 1.3 秒。ci
#include<bits/stdc++.h> using namespace std; int n; long long k; vector<int> v1, v2; vector<long long> res1, res2; void dfs(int i, long long x, long long r, vector<int> v, vector<long long>& res) { res.push_back(x); for(int j = i; j < v.size(); j++) { if(v[j] <= r / x) dfs(j, x * v[j], r, v, res); else break; } } long long judge(long long r) { int s = res2.size() - 1; long long ans = 0; for (int i = 0; i < res1.size(); i++) { while(s >= 0 && res2[s] > r / res1[i]) s--; ans += s + 1; } return ans; } int main() { cin >> n; for (int i = 0; i < n; i++) { int p; cin >> p; !(i & 1) ? v1.push_back(p) : v2.push_back(p); } cin >> k; res1.clear(); res2.clear(); dfs(0, 1, 1e18, v1, res1); dfs(0, 1, 1e18, v2, res2); sort(res1.begin(), res1.end()); sort(res2.begin(), res2.end()); long long l = 1, r = 1e18; while(l < r) { long long mid = l + r >> 1; if(judge(mid) < k) l = mid + 1; else r = mid; } cout << l << endl; return 0; }