Loading [MathJax]/extensions/tex2jax.js

「Fortuna OJ」Jul 5th – Group A 解题报告 / 集训队互测 2013

A – 家族

这道题真的是送分题(快要想出来直接暴力枚举+并查集的时候去看了题解,最后发现就是这么 sb)。

考虑枚举频段区间\([L, R]\)(将边进行排序,确定下界之后再枚举上界),这个地方是\(O(m^2)\)的。每次枚举下界的时候都要初始化并查集,然后合并两个集合的时候按照大小来修改答案就行了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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\),根据这个式子可以加一条新边。判断是否可行即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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 – 城市规划

等我把多项式和卷积之类的东西全部搞懂再来写这道毒瘤题吧。

Available Here

Leave a Reply

Your email address will not be published. Required fields are marked *