[题目链接]
铺设道路 : https://www.luogu.org/problemnew/show/P5019
货币系统 : https://www.luogu.org/problemnew/show/P5020
赛道修建 : https://www.luogu.org/problemnew/show/P5021
[题解]
Problem A 铺设道路
首先 , 我们有一个分治的思路 , 对于一段区间[l , r] , 最优策略为将区间内的所有数都减掉区间中的最小值 , 然后将该问题分成两个子问题 , 分治下去即可 , 时间复杂度 : O(N ^ 2)
用ST表预处理区间最小值 , 分治时实现O(1)查询 , 时间复杂度优化为 O(NlogN)
我们还可以使用贪心算法 , 如果第i个数 > 第(i - 1)个数 , 则它们对答案产生了(Hi - Hi-1)的“贡献” , 贡献相加即为答案 , 证明略 , 时间复杂度 : O(N)
Problem B 货币系统
首先有一个结论 : 在最优的情况下 , 必然满足m为n的子集 , 这个结论是显然的
根据这个结论 , 我们不妨将a数组按升序排序 , 若第(k + 1)个数能由前k个数组合出 , 则删除第(k + 1)个数
具体实现时可以完全背包 , 时间复杂度 : O(TNW)(取W = 25000)
Problem C 赛道修建
要求长度最小的赛道长度尽可能大 , 我们不妨二分答案limit
对于以u为根节点的一棵子树 , 我们可以记录每一条连接到u的赛道的长度value , 和这条路径的长度u ,若 :
1. value + w >= limit , 将答案加一 , 单独构成一条赛道
2. value + w < limit , 对于这种情况 , 我们将(value + w)插入到一棵平衡树(可以方便使用std :: multimap)
然后 , 对于平衡树中的每个数w , 我们贪心地将找到最小的v , 使得w + v >= limit , 并在平衡树中删除w和v
最后我们只要判断路径条数是否 >= m , 即可
时间复杂度 : O(NlogN ^ 2) , 注意优化常数 , 如二分上界可定为树的直径
[代码]
铺设道路 :
// Sprease Table O(NlogN) #include<bits/stdc++.h> using namespace std; #define MAXN 100010 const int MAXLOG = 20; int n; long long ans; int a[MAXN]; int mn[MAXN][MAXLOG] , loc[MAXN][MAXLOG]; template <typename T> inline void chkmin(T &x , T y) { x = min(x , y); } template <typename T> inline void chkmax(T &x , T y) { x = max(x , y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline pair<int , int> query(int l , int r) { int k = (int)((double)log(r - l + 1) / log(2.0)); int ret1 = min(mn[l][k] , mn[r - (1 << k) + 1][k]); int ret2; if (mn[l][k] < mn[r - (1 << k) + 1][k]) ret2 = loc[l][k]; else ret2 = loc[r - (1 << k) + 1][k]; return make_pair(ret1 , ret2); } inline void solve(int l , int r , int k) { if (l > r) return; pair<int , int> tmp = query(l , r); ans += tmp.first - k; solve(l , tmp.second - 1 , tmp.first); solve(tmp.second + 1 , r , tmp.first); } int main() { read(n); for (int i = 1; i <= n; i++) { read(a[i]); mn[i][0] = a[i]; loc[i][0] = i; } for (int j = 1; j < MAXLOG; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) { mn[i][j] = min(mn[i][j - 1] , mn[i + (1 << (j - 1))][j - 1]); if (mn[i][j - 1] < mn[i + (1 << (j - 1))][j - 1]) loc[i][j] = loc[i][j - 1]; else loc[i][j] = loc[i + (1 << (j - 1))][j - 1]; } } solve(1 , n , 0); cout<< ans << " "; return 0; }
货币系统 :
#include<bits/stdc++.h> using namespace std; #define MAXN 110 int n; int a[MAXN]; bool dp[35520]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { int T; read(T); while (T--) { read(n); for (int i = 1; i <= n; i++) read(a[i]); memset(dp , false , sizeof(dp)); dp[0] = true; int ans = 0; sort(a + 1 , a + n + 1); for (int i = 1; i <= n; i++) { if (dp[a[i]]) continue; ++ans; for (int j = a[i]; j <= 25000; j++) { dp[j] |= dp[j - a[i]]; } } printf("%d " , ans); } return 0; }
赛道修建 :
#include<bits/stdc++.h> using namespace std; #define MAXN 50010 typedef long long ll; typedef long double ld; const ll inf = 1e15; struct edge { int to , w , nxt; } e[MAXN << 1]; int n , m , tot , cnt; int head[MAXN]; ll dist[MAXN]; multiset< ll > M[MAXN]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline void addedge(int u , int v , int w) { ++tot; e[tot] = (edge){v , w , head[u]}; head[u] = tot; } inline ll dfs(int u , int father , ll limit) { M[u].clear(); for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to , w = e[i].w; if (v == father) continue; ll val = dfs(v , u , limit) + (ll)w; if (val >= limit) ++cnt; else M[u].insert(val); } ll ret = 0; while (!M[u].empty()) { if ((int)M[u].size() == 1) { chkmax(ret , *M[u].begin()); break; } else { ll tmp = *M[u].begin(); M[u].erase(M[u].begin()); multiset< ll > :: iterator it = M[u].lower_bound(limit - tmp); if (it == M[u].end()) { chkmax(ret , tmp); } else { ++cnt; M[u].erase(it); } } } return ret; } inline ll diameter() { queue< int > q; for (int i = 1; i <= n; i++) dist[i] = inf; dist[1] = 0; q.push(1); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to , w = e[i].w; if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; q.push(v); } } } int s = 1; for (int i = 1; i <= n; i++) if (dist[i] > dist[s]) s = i; for (int i = 1; i <= n; i++) dist[i] = inf; dist[s] = 0; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to , w = e[i].w; if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; q.push(v); } } } ll ret = 0; for (int i = 1; i <= n; i++) chkmax(ret , dist[i]); return ret; } inline bool check(ll mid) { cnt = 0; dfs(1 , 0 , mid); return cnt >= m; } int main() { read(n); read(m); ll l = 0 , r = 0 , ans = 0; for (int i = 1; i < n; i++) { int u , v , w; read(u); read(v); read(w); addedge(u , v , w); addedge(v , u , w); } r = diameter(); while (l <= r) { ll mid = (l + r) >> 1; if (check(mid)) { ans = mid; l = mid + 1; } else r = mid - 1; } printf("%lld " , ans); return 0; }