A – 家族
这道题真的是送分题(快要想出来直接暴力枚举+并查集的时候去看了题解,最后发现就是这么 sb)。
考虑枚举频段区间\([L, R]\)(将边进行排序,确定下界之后再枚举上界),这个地方是\(O(m^2)\)的。每次枚举下界的时候都要初始化并查集,然后合并两个集合的时候按照大小来修改答案就行了。
// A.cpp #include <bits/stdc++.h> #define ll long long using namespace std; const ll INF = 0x3f3f3f3f3f3f3f3f, MAX_N = 1010, MAX_E = 5050; ll n, m, k, ans = INF, fa[MAX_N], current_ans, siz[MAX_N], dat[MAX_N]; struct edge { ll from, to, frequency; bool operator<(const edge& e) const { return frequency < e.frequency; } } edges[MAX_E]; void ufs_initialize() { for (ll i = 1; i <= n; i++) fa[i] = i, siz[i] = 1; } ll find(ll x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void unite(ll x, ll y) { x = find(x), y = find(y); if (x == y) return; if (siz[x] < siz[y]) swap(x, y); current_ans -= dat[siz[x]] + dat[siz[y]]; fa[y] = fa[x], siz[x] += siz[y]; current_ans += dat[siz[x]]; } signed main() { scanf("%lld%lld%lld", &n, &m, &k); for (ll i = 1; i <= n; i++) scanf("%lld", &dat[i]); for (ll i = 1; i <= m; i++) scanf("%lld%lld%lld", &edges[i].from, &edges[i].to, &edges[i].frequency); sort(edges + 1, edges + 1 + m); for (ll lbound = 1, lower = edges[1].frequency; lbound <= m; lbound++, lower = edges[lbound].frequency) { ufs_initialize(), current_ans = dat[1] * n; for (ll rbound = lbound, upper = edges[rbound].frequency; rbound <= m; rbound++, upper = edges[rbound].frequency) { ll width = upper - lower; unite(edges[rbound].from, edges[rbound].to); if (current_ans >= k) { ans = min(ans, width); break; } } } if (ans == INF) puts("T_T"); else printf("%lld", ans); return 0; }
B – 供电网络
第二题就开始毒瘤了起来,把题意精简之后就是这样的:
给定一个有源汇点的上下界网络,其中每一条边的费用是关于流量的二次关系。求最小费用可行流即可。
首先忽略二次关系,这道题单建模是很简单的,标准的有源汇的上下界可行流模型,根据每个城市的结余连接超级源点和汇点,根据每个城市购入和输出电力的价格连接源点和汇点。
之后,我们可以把连续的二次关系离散化:也就是,恒定流过的流量为\(1\),然后每次求完最短路、统计答案之后,考虑继续加边:流量上界为\(1\)下界为\(0\),且费用为当前与上次流过的差值,也就是\(a_i (x + 1)^2 – b_i (x + 1) – a_i x^2 – b_i x_i = 2 a_i x _+ a_i + b_i\),根据这个式子可以加一条新边。判断是否可行即可。
// B.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 330, MAX_E = 5050, INF = 0x3f3f3f3f; int n, m, head[MAX_N], current, di[MAX_N], s, t, S, T, answer, flag[MAX_N][MAX_N]; int dist[MAX_N], upward[MAX_N], flow[MAX_N]; bool vis[MAX_N]; struct edge { int to, nxt, weight, cost; } edges[MAX_E << 1]; struct side { int u, v, a, b, L, R; } original_data[MAX_E << 1]; void addpath(int src, int dst, int weight, int cost) { edges[current].to = dst, edges[current].nxt = head[src]; edges[current].weight = weight, edges[current].cost = cost; head[src] = current++; } void addtube(int src, int dst, int lower, int upper, int cost, bool doDi = true) { addpath(src, dst, upper - lower, cost), addpath(dst, src, 0, -cost); if (doDi) di[dst] += lower, di[src] -= lower; } bool spfa() { for (int i = 1; i <= m; i++) if (flag[original_data[i].u][original_data[i].v] == original_data[i].L && original_data[i].L < original_data[i].R) { original_data[i].L++; addtube(original_data[i].u, original_data[i].v, 0, 1, original_data[i].a * (2 * original_data[i].L - 1) + original_data[i].b, false); } memset(dist, 0x3f, sizeof(dist)); queue<int> q; q.push(S), dist[S] = 0, vis[S] = true; while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = false; for (int i = head[u]; i != -1; i = edges[i].nxt) if (dist[edges[i].to] > dist[u] + edges[i].cost && edges[i].weight > 0) { dist[edges[i].to] = dist[u] + edges[i].cost, upward[edges[i].to] = i; if (!vis[edges[i].to]) vis[edges[i].to] = true, q.push(edges[i].to); } } return dist[T] != INF; } void find() { int u = T, i = upward[u]; while (u != S) { edges[i].weight--, edges[i ^ 1].weight++; answer += edges[i].cost; flag[edges[i ^ 1].to][edges[i].to]++, flag[edges[i].to][edges[i ^ 1].to]--; u = edges[i ^ 1].to, i = upward[u]; } } int main() { memset(head, -1, sizeof(head)); scanf("%d%d", &n, &m); s = n + 1, t = s + 1, S = t + 1, T = S + 1; for (int i = 1, lft, in, out; i <= n; i++) { scanf("%d%d%d", &lft, &in, &out); if (lft > 0) addtube(s, i, lft, lft, 0); else if (lft < 0) addtube(i, t, -lft, -lft, 0); addtube(s, i, 0, INF, in), addtube(i, t, 0, INF, out); } for (int i = 1; i <= m; i++) { scanf("%d%d%d%d%d%d", &original_data[i].u, &original_data[i].v, &original_data[i].a, &original_data[i].b, &original_data[i].L, &original_data[i].R); addtube(original_data[i].u, original_data[i].v, original_data[i].L, original_data[i].L, 0); answer += original_data[i].L * (original_data[i].a * original_data[i].L + original_data[i].b); flag[original_data[i].u][original_data[i].v] += original_data[i].L; flag[original_data[i].v][original_data[i].u] -= original_data[i].L; } // in searching the possible stream; addtube(t, s, 0, INF, 0); for (int i = 1; i <= t; i++) if (di[i] > 0) addtube(S, i, 0, di[i], 0); else if (di[i] < 0) addtube(i, T, 0, -di[i], 0); while (spfa()) find(); printf("%d", answer); return 0; }
C – 城市规划
等我把多项式和卷积之类的东西全部搞懂再来写这道毒瘤题吧。