"2019 Jizhong training Day12" problem-solving report

T1, maze

A $ n \ times m (n \ leq 5, m \ leq 10 ^ 5) $ matrix \ (0 \) represents the lattice can not go, \ (1 \) represents the lattice can go only up, down, right go in three directions. There $ q (q \ leq 5 \ times 10 ^ 4) $ operations, there are two operations:
1, a modified type of the lattice;
2, whether the query from $ (a, b) $ reaches \ ((C , d) \) (to the right of the former to ensure the latter).

\(Sol\)

Observed \ (the n-\ Leq 5 \) , it is very important tips.
Segment tree built in the lateral direction, each segment tree leaf node as a matrix, which describes the relationship between the communication points on the column, in particular, if the lattice columns of a row can not take it into their own in the matrix should be expressed as unreachable;
enumeration combined intermediate point \ (n ^ 3 \) violence combined.
But I wrote the examination room a large practice constant: each matrix leaf node connectivity relationship described in this column next column, empathy merger; to change two points written modify, and then gone.

\(Source\)

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;

int n, m, q, mp[7][N];

template<typename T>inline void chk_min(T &_, T __) {
    _ = !~_ ? __ : (_ < __ ? _ : __);
}
struct node {
    int a[6][6];
    node () {
        memset(a, -1, sizeof(a));
    }
    inline int* operator [] (const int x) {
        return a[x];
    }
    inline node operator + (node b) const {
        node ret;
        for (int k = 1; k <= n; ++k)
            for (int i = 1; i <= n; ++i)
                if (~a[i][k])
                    for (int j = 1; j <= n; ++j)
                        if (~b[k][j])
                            chk_min(ret[i][j], a[i][k] + b[k][j] + 1);
        return ret;
    }
} ;
struct segment_tree {
    node t[N << 2];
    void init(int x, int p) {
        for (int i = 1, j; i <= n; ++i) {
            for (j = i; j <= n && mp[j][x]; ++j)
                t[p][i][j] = j - i;
            for (; j <= n; ++j)
                t[p][i][j] = -1;
            for (j = i; j && mp[j][x]; --j)
                t[p][i][j] = i - j;
            for (; j; --j)
                t[p][i][j] = -1;
        }
    }
    inline void push_up(int p) {
        t[p] = t[p << 1] + t[p << 1 | 1];
    }
    void build(int tl, int tr, int p) {
        if (tl == tr)
            return init(tl, p);
        int mid = (tl + tr) >> 1;
        build(tl, mid, p << 1), build(mid + 1, tr, p << 1 | 1);
        push_up(p);
    }
    void modify(int pos, int tl, int tr, int p) {
        if (tl == tr)
            return init(tl, p);
        int mid = (tl + tr) >> 1;
        if (mid >= pos)
            modify(pos, tl, mid, p << 1);
        else
            modify(pos, mid + 1, tr, p << 1 | 1);
        push_up(p);
    }
    node query(int l, int r, int tl, int tr, int p) {
        if (l <= tl && tr <= r)
            return t[p];
        int mid = (tl + tr) >> 1;
        if (mid < l)
            return query(l, r, mid + 1, tr, p << 1 | 1);
        if (mid >= r)
            return query(l, r, tl, mid, p << 1);
        return query(l, r, tl, mid, p << 1) +
               query(l, r, mid + 1, tr, p << 1 | 1);
    }
} T;

int main() {
    //freopen("in", "r", stdin);
    freopen("maze.in", "r", stdin);
    freopen("maze.out", "w", stdout);
    n = in(), m = in(), q = in();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            mp[i][j] = in();
    T.build(1, m, 1);

    int a, b, c, d;
    node res;
    while (q--) {
        if (in() == 1) {
            a = in(), b = in();
            mp[a][b] ^= 1;
            T.modify(b, 1, m, 1);
        } else {
            a = in(), b = in(), c = in(), d = in();
            res = T.query(b, d, 1, m, 1);
            printf("%d\n", res[a][c]);
        }
    }
    return 0;
}

T2, Han Wang Meng

There plane \ (n \ (n \ leq 10 ^ 5) \) black dots, \ (m \ (n-\ Leq 10 ^. 5) \) white spots, when the black dot \ (X \) and white point \ (Y \) is the Manhattan distance less \ (D \ (D \ Leq 10 ^. 9) \) , then \ (X \) to \ (Y \) is connected edges, or \ (Y \) to \ (X \) even edge.
Even the same direction between the color point of uncertainty, at least find a minimum and maximum black dots and white dots three-membered ring.

\(Sol\)

Obviously, there are two points of the same color on the three-membered ring.
Because even Leis side is how we do not care, only care about whether the edge of the ring, so black and white dots empathy for the same.
Note \ (cover (x) \) represents \ (X \) can cover the white point, \ (Cover (X, Y) \) represents \ (x, y \) together cover the white point.
The following Example the maximum value (minimum empathy).

\[ \begin{align} Ans = & \sum_{x, y \in S} \max \{cover(x) - cover{(x,y)}, cover(y) - cover(x, y) \} \\ = & \sum_{x, y \in S} \max \{ cover(x), cover(y) \} - \sum_{x, y \in S} cover(x, y) \\ = & \sum_{x, y \in S} \max \{ cover(x), cover(y) \} - \sum_{x \in S} \binom{cover(x)}{2} \\ \end{align} \]

As long as calculated \ (cover (x) \) can be;
scanning lines maintain the following on the line.

\(Source\)

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 1e5 + 5;
struct node {
    int x, y;
} a[N], b[N];
struct options {
    long long x; int y, z, id;
    int typ;
} opt[N * 3];

int n, m, d, nn, cntx[N], cnty[N];
long long ans_min, ans_max;

long long tmp[N * 3];
inline bool cmpx(const options &i, const options &j) {
    if (i.x == j.x)
        return i.typ == 0 ? 1 : j.typ != 0;
    return i.x < j.x;
}
void prep() {
    nn = 0;
    for (int i = 1; i <= m; ++i)
        tmp[i] = b[i].y;
    for (int i = 1; i <= n; ++i)
        tmp[i + m] = 1ll * a[i].y - d;
    for (int i = 1; i <= n; ++i)
        tmp[i + n + m] = 1ll * a[i].y + d;
    std::sort(tmp + 1, tmp + 1 + n + n + m);
    nn = std::unique(tmp + 1, tmp + 1 + n + n + m) - tmp - 1;
    for (int i = 1; i <= m; ++i) {
        opt[i].typ = opt[i].id = 0;
        opt[i].x = b[i].x;
        opt[i].y = std::lower_bound(tmp + 1, tmp + 1 + nn, b[i].y) - tmp;
        opt[i].z = 0;
    }
    for (int i = 1; i <= n; ++i) {
        opt[i + m].typ = -1;
        opt[i + m].id = i;
        opt[i + m].x = 1ll * a[i].x - d - 1;

        opt[i + n + m].typ = 1;
        opt[i + n + m].id = i;
        opt[i + n + m].x = 1ll * a[i].x + d;
        
        opt[i + m].y = opt[i + n + m].y = std::lower_bound(tmp + 1, tmp + 1 + nn, 1ll * a[i].y - d) - tmp;
        opt[i + m].z = opt[i + n + m].z = std::lower_bound(tmp + 1, tmp + 1 + nn, 1ll * a[i].y + d) - tmp;
    }
    std::sort(opt + 1, opt + 1 + n + n + m, cmpx);
}

struct binary_index_tree {
    int a[N * 3];
    inline void init() { memset(a, 0, sizeof(a)); }
    void insert(int p, int k) { for (; p <= nn; p += (p & -p)) a[p] += k; }
    int ask(int p, int ret = 0) { for (; p; p -= (p & -p)) ret += a[p]; return ret; }
} bit;

void work() {
    prep();
    bit.init();
    for (int i = 1; i <= n + n + m; ++i) {
        if (!opt[i].typ) {
            bit.insert(opt[i].y, 1);
        } else {
            cntx[opt[i].id] += opt[i].typ * (bit.ask(opt[i].z) - bit.ask(opt[i].y - 1));
        }
    }
}

int main() {
    //freopen("in", "r", stdin);
    freopen("mhw.in", "r", stdin);
    freopen("mhw.out", "w", stdout);
    n = in(), m = in(), d = in();
    for (int i = 1; i <= n; ++i) {
        a[i] = (node){in(), in()};
        a[i] = (node){a[i].x + a[i].y, a[i].x - a[i].y};
    }
    for (int i = 1; i <= m; ++i) {
        b[i] = (node){in(), in()};
        b[i] = (node){b[i].x + b[i].y, b[i].x - b[i].y};
    }
    work();
    std::swap(a, b);
    std::swap(n, m);
    std::swap(cntx, cnty);
    work();

    std::sort(cntx + 1, cntx + 1 + n);
    std::sort(cnty + 1, cnty + 1 + m);
    for (int i = 1; i <= n; ++i) {
        ans_min += 1ll * cntx[i] * (n - i);
        ans_min -= 1ll * cntx[i] * (cntx[i] - 1) / 2;
        ans_max += 1ll * cntx[i] * (i - 1);
        ans_max -= 1ll * cntx[i] * (cntx[i] - 1) / 2;
    }
    for (int i = 1; i <= m; ++i) {
        ans_min += 1ll * cnty[i] * (m - i);
        ans_min -= 1ll * cnty[i] * (cnty[i] - 1) / 2;
        ans_max += 1ll * cnty[i] * (i - 1);
        ans_max -= 1ll * cnty[i] * (cnty[i] - 1) / 2;
    }
    printf("%lld %lld\n", ans_min, ans_max);
    return 0;
}

T1, factory

Given a bipartite graph can be edged so that all maximal matching bipartite graph is a perfect match.

\(Sol\)

To prove one thing: if and only if the same about this bipartite graph in each block Unicom points and edges are even full of \ ((A) \) , to meet the meaning of the questions \ ((B) \) .
By the \ (B \) to \ (A \) be apparent, will not be repeated;
a \ (A \) to \ (B \) may be by contradiction:
If there are two points opposite side Unicom block \ (A \ ) , \ (B \) is not connected between the edges found from a \ (a \) to \ (B \) path, the odd number of sides matching the set of edges added, to find a match based on this great, found should be a perfect match; odd side matching edge set on the path taken, even number of sides is added, but this time is a maximal matching is not a perfect match.
\ (QED \)
Consideration Unicom several blocks are merged as \ ((\ SUM x_i) ^ 2 \) .
Note \ (f_ {s, i} \) indicates that the current block is set as the selected link \ (S \) , and has been arranged to point \ (2i \) Unicom blocks (so that they satisfy the above criteria) minimum cost.
Transfer two ways:
\ [\ Begin {align} f_ {s \ bigcup {x}, i} = & \ min (f_ {s \ bigcup {x}, i}, f_ {S, i}) \\ f_ {s, i} = & \ min_ {j = 0 } ^ {i - 1} \ {f_ {s, j} + (i - j) ^ 2 \} \, (\ Sigma_ {x \ in s} = \ Sigma_ {y \ in s}) \ end {align
} \] Since the number of states too, so too can not be optimized manner like the pressure;
we are concerned only Unicom each block (left-right classification points) there are a few, can be changed decimal to optimize the number of states;
the topic and people have told us the most \ (172,032 \) states.

\(Source\)

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 35, inf = 0x3f3f3f3f;

int n, m, mp[N << 1][N << 1], tmp_x, tmp_y, init_edge, buc[N][N], pre[N << 1], f[172033][N], num[N << 1];
bool vis[N << 1];

struct node {
    int x, y;
    inline bool operator < (const node &b) const {
        return buc[this->x][this->y] < buc[b.x][b.y];
    }
    inline bool operator == (const node &b) const {
        return this->x == b.x && this->y == b.y;
    }
} a[N + N];

void prep(const int u) {
    vis[u] = 1;
    if (u <= n)
        ++tmp_x;
    else
        ++tmp_y;
    for (int i = 1; i <= mp[u][0]; ++i)
        if (!vis[mp[u][i]])
            prep(mp[u][i]);
}

int main() {
    //freopen("in", "r", stdin);
    freopen("factory.in", "r", stdin);
    freopen("factory.out", "w", stdout);
    n = in();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) {
            char c = getchar();
            while (c < '0' || c > '1')
                c = getchar();
            if (c == '1') {
                mp[i][++mp[i][0]] = j + n;
                mp[j + n][++mp[j + n][0]] = i;
            }
            init_edge += c == '1';
        }
    for (int i = 1; i <= n + n; ++i)
        if (!vis[i]) {
            tmp_x = tmp_y = 0;
            prep(i);
            ++buc[tmp_x][tmp_y];
        }

    for (int i = 0; i <= n; ++i)
        for (int j = 0; j <= n; ++j)
            if (buc[i][j])
                a[++m] = (node){i, j};
    std::sort(a + 1, a + 1 + m);
    m = std::unique(a + 1, a + 1 + m) - a - 1;
    pre[1] = 1;
    for (int i = 2; i <= m + 1; ++i)
        pre[i] = pre[i - 1] * (buc[a[i - 1].x][a[i - 1].y] + 1);

    memset(f, inf, sizeof(f));
    f[0][0] = 0;

    int nowx, nowy;
    for (int s = 0; s < (pre[m + 1]); ++s) {
        nowx = nowy = 0;
        for (int i = 1; i <= m; ++i)
            num[i] = s % pre[i + 1] / pre[i];
        for (int i = 1; i <= m; ++i)
            nowx += num[i] * a[i].x, nowy += num[i] * a[i].y;
        if (nowx == nowy)
            for (int i = 0; i < nowx; ++i)
                chk_min(f[s][nowx], f[s][i] + (nowx - i) * (nowx - i));
        for (int i = 1; i <= m; ++i)
            if (num[i] < buc[a[i].x][a[i].y])
                for (int j = 0; j <= n; ++j)
                    chk_min(f[s + pre[i]][j], f[s][j]);
    }
    printf("%d\n", f[pre[m + 1] - 1][n] - init_edge);

    return 0;
}

T1, maze

A $ n \ times m (n \ leq 5, m \ leq 10 ^ 5) $ matrix \ (0 \) represents the lattice can not go, \ (1 \) represents the lattice can go only up, down, right go in three directions. There $ q (q \ leq 5 \ times 10 ^ 4) $ operations, there are two operations:
1, a modified type of the lattice;
2, whether the query from $ (a, b) $ reaches \ ((C , d) \) (to the right of the former to ensure the latter).

\(Sol\)

Observed \ (the n-\ Leq 5 \) , it is very important tips.
Segment tree built in the lateral direction, each segment tree leaf node as a matrix, which describes the relationship between the communication points on the column, in particular, if the lattice columns of a row can not take it into their own in the matrix should be expressed as unreachable; enumeration combined intermediate point \ (n ^ 3 \) violence combined.
But I wrote the examination room a large practice constant: each matrix leaf node connectivity relationship described in this column next column, empathy merger; to change two points written modify, and then gone.

\(Source\)

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;

int n, m, q, mp[7][N];

template<typename T>inline void chk_min(T &_, T __) {
    _ = !~_ ? __ : (_ < __ ? _ : __);
}
struct node {
    int a[6][6];
    node () {
        memset(a, -1, sizeof(a));
    }
    inline int* operator [] (const int x) {
        return a[x];
    }
    inline node operator + (node b) const {
        node ret;
        for (int k = 1; k <= n; ++k)
            for (int i = 1; i <= n; ++i)
                if (~a[i][k])
                    for (int j = 1; j <= n; ++j)
                        if (~b[k][j])
                            chk_min(ret[i][j], a[i][k] + b[k][j] + 1);
        return ret;
    }
} ;
struct segment_tree {
    node t[N << 2];
    void init(int x, int p) {
        for (int i = 1, j; i <= n; ++i) {
            for (j = i; j <= n && mp[j][x]; ++j)
                t[p][i][j] = j - i;
            for (; j <= n; ++j)
                t[p][i][j] = -1;
            for (j = i; j && mp[j][x]; --j)
                t[p][i][j] = i - j;
            for (; j; --j)
                t[p][i][j] = -1;
        }
    }
    inline void push_up(int p) {
        t[p] = t[p << 1] + t[p << 1 | 1];
    }
    void build(int tl, int tr, int p) {
        if (tl == tr)
            return init(tl, p);
        int mid = (tl + tr) >> 1;
        build(tl, mid, p << 1), build(mid + 1, tr, p << 1 | 1);
        push_up(p);
    }
    void modify(int pos, int tl, int tr, int p) {
        if (tl == tr)
            return init(tl, p);
        int mid = (tl + tr) >> 1;
        if (mid >= pos)
            modify(pos, tl, mid, p << 1);
        else
            modify(pos, mid + 1, tr, p << 1 | 1);
        push_up(p);
    }
    node query(int l, int r, int tl, int tr, int p) {
        if (l <= tl && tr <= r)
            return t[p];
        int mid = (tl + tr) >> 1;
        if (mid < l)
            return query(l, r, mid + 1, tr, p << 1 | 1);
        if (mid >= r)
            return query(l, r, tl, mid, p << 1);
        return query(l, r, tl, mid, p << 1) +
               query(l, r, mid + 1, tr, p << 1 | 1);
    }
} T;

int main() {
    //freopen("in", "r", stdin);
    freopen("maze.in", "r", stdin);
    freopen("maze.out", "w", stdout);
    n = in(), m = in(), q = in();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            mp[i][j] = in();
    T.build(1, m, 1);

    int a, b, c, d;
    node res;
    while (q--) {
        if (in() == 1) {
            a = in(), b = in();
            mp[a][b] ^= 1;
            T.modify(b, 1, m, 1);
        } else {
            a = in(), b = in(), c = in(), d = in();
            res = T.query(b, d, 1, m, 1);
            printf("%d\n", res[a][c]);
        }
    }
    return 0;
}

T2, Han Wang Meng

There plane \ (n \ (n \ leq 10 ^ 5) \) black dots, \ (m \ (n-\ Leq 10 ^. 5) \) white spots, when the black dot \ (X \) and white point \ (Y \) is the Manhattan distance less \ (D \ (D \ Leq 10 ^. 9) \) , then \ (X \) to \ (Y \) is connected edges, or \ (Y \) to \ (X \) even edge.
Even the same direction between the color point of uncertainty, at least find a minimum and maximum black dots and white dots three-membered ring.

\(Sol\)

Obviously, there are two points of the same color on the three-membered ring.
Because even Leis side is how we do not care, only care about whether the edge of the ring, so black and white dots empathy for the same.
Note \ (cover (x) \) represents \ (X \) can cover the white point, \ (Cover (X, Y) \) represents \ (x, y \) together cover the white point.
The following Example the maximum value (minimum empathy).

\[ \begin{align} Ans = & \sum_{x, y \in S} \max \{cover(x) - cover{(x,y)}, cover(y) - cover(x, y) \} \\ = & \sum_{x, y \in S} \max \{ cover(x), cover(y) \} - \sum_{x, y \in S} cover(x, y) \\ = & \sum_{x, y \in S} \max \{ cover(x), cover(y) \} - \sum_{x \in S} \binom{cover(x)}{2} \\ \end{align} \]

As long as calculated \ (cover (x) \) can be;
scanning line maintenance on the line.

\(Source\)

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 1e5 + 5;
struct node {
    int x, y;
} a[N], b[N];
struct options {
    long long x; int y, z, id;
    int typ;
} opt[N * 3];

int n, m, d, nn, cntx[N], cnty[N];
long long ans_min, ans_max;

long long tmp[N * 3];
inline bool cmpx(const options &i, const options &j) {
    if (i.x == j.x)
        return i.typ == 0 ? 1 : j.typ != 0;
    return i.x < j.x;
}
void prep() {
    nn = 0;
    for (int i = 1; i <= m; ++i)
        tmp[i] = b[i].y;
    for (int i = 1; i <= n; ++i)
        tmp[i + m] = 1ll * a[i].y - d;
    for (int i = 1; i <= n; ++i)
        tmp[i + n + m] = 1ll * a[i].y + d;
    std::sort(tmp + 1, tmp + 1 + n + n + m);
    nn = std::unique(tmp + 1, tmp + 1 + n + n + m) - tmp - 1;
    for (int i = 1; i <= m; ++i) {
        opt[i].typ = opt[i].id = 0;
        opt[i].x = b[i].x;
        opt[i].y = std::lower_bound(tmp + 1, tmp + 1 + nn, b[i].y) - tmp;
        opt[i].z = 0;
    }
    for (int i = 1; i <= n; ++i) {
        opt[i + m].typ = -1;
        opt[i + m].id = i;
        opt[i + m].x = 1ll * a[i].x - d - 1;

        opt[i + n + m].typ = 1;
        opt[i + n + m].id = i;
        opt[i + n + m].x = 1ll * a[i].x + d;
        
        opt[i + m].y = opt[i + n + m].y = std::lower_bound(tmp + 1, tmp + 1 + nn, 1ll * a[i].y - d) - tmp;
        opt[i + m].z = opt[i + n + m].z = std::lower_bound(tmp + 1, tmp + 1 + nn, 1ll * a[i].y + d) - tmp;
    }
    std::sort(opt + 1, opt + 1 + n + n + m, cmpx);
}

struct binary_index_tree {
    int a[N * 3];
    inline void init() { memset(a, 0, sizeof(a)); }
    void insert(int p, int k) { for (; p <= nn; p += (p & -p)) a[p] += k; }
    int ask(int p, int ret = 0) { for (; p; p -= (p & -p)) ret += a[p]; return ret; }
} bit;

void work() {
    prep();
    bit.init();
    for (int i = 1; i <= n + n + m; ++i) {
        if (!opt[i].typ) {
            bit.insert(opt[i].y, 1);
        } else {
            cntx[opt[i].id] += opt[i].typ * (bit.ask(opt[i].z) - bit.ask(opt[i].y - 1));
        }
    }
}

int main() {
    //freopen("in", "r", stdin);
    freopen("mhw.in", "r", stdin);
    freopen("mhw.out", "w", stdout);
    n = in(), m = in(), d = in();
    for (int i = 1; i <= n; ++i) {
        a[i] = (node){in(), in()};
        a[i] = (node){a[i].x + a[i].y, a[i].x - a[i].y};
    }
    for (int i = 1; i <= m; ++i) {
        b[i] = (node){in(), in()};
        b[i] = (node){b[i].x + b[i].y, b[i].x - b[i].y};
    }
    work();
    std::swap(a, b);
    std::swap(n, m);
    std::swap(cntx, cnty);
    work();

    std::sort(cntx + 1, cntx + 1 + n);
    std::sort(cnty + 1, cnty + 1 + m);
    for (int i = 1; i <= n; ++i) {
        ans_min += 1ll * cntx[i] * (n - i);
        ans_min -= 1ll * cntx[i] * (cntx[i] - 1) / 2;
        ans_max += 1ll * cntx[i] * (i - 1);
        ans_max -= 1ll * cntx[i] * (cntx[i] - 1) / 2;
    }
    for (int i = 1; i <= m; ++i) {
        ans_min += 1ll * cnty[i] * (m - i);
        ans_min -= 1ll * cnty[i] * (cnty[i] - 1) / 2;
        ans_max += 1ll * cnty[i] * (i - 1);
        ans_max -= 1ll * cnty[i] * (cnty[i] - 1) / 2;
    }
    printf("%lld %lld\n", ans_min, ans_max);
    return 0;
}

T1, factory

Given a bipartite graph can be edged so that all maximal matching bipartite graph is a perfect match.

\(Sol\)

To prove one thing: if and only if the same about this bipartite graph in each block Unicom points and edges are even full of \ ((A) \) , to meet the meaning of the questions \ ((B) \) .
By the \ (B \) to \ (A \) be apparent, will not be repeated;
a \ (A \) to \ (B \) may be by contradiction:
If there are two points opposite side Unicom block \ (A \ ) , \ (B \) is not connected between the edges found from a \ (a \) to \ (B \) path, the odd number of sides matching the set of edges added, to find a match based on this great, found should be a perfect match;
odd side matching edge set on the path taken, even number of sides is added, but this time is a maximal matching is not a perfect match.
\ (QED \)
Consideration Unicom several blocks are merged as \ ((\ SUM x_i) ^ 2 \) .
Note \ (f_ {s, i} \) indicates that the current block is set as the selected link \ (S \) , and has been arranged to point \ (2i \) Unicom blocks (so that they satisfy the above criteria) minimum cost.
Transfer two ways:
\ [\ Begin {align} f_ {s \ bigcup {x}, i} = & \ min (f_ {s \ bigcup {x}, i}, f_ {S, i}) \\ f_ {s, i} = & \ min_ {j = 0 } ^ {i - 1} \ {f_ {s, j} + (i - j) ^ 2 \} \, (\ Sigma_ {x \ in s} = \ Sigma_ {y \ in s}) \ end {align
} \] Since the number of states too, so too can not be optimized manner like the pressure;
we are concerned only Unicom each block (left-right classification points) there are a few, can be changed decimal to optimize the number of states;
the topic and people have told us the most \ (172,032 \) states.

\(Source\)

#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
    int x = 0; char c = getchar(); bool f = 0;
    while (c < '0' || c > '9')
        f |= c == '-', c = getchar();
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }

const int N = 35, inf = 0x3f3f3f3f;

int n, m, mp[N << 1][N << 1], tmp_x, tmp_y, init_edge, buc[N][N], pre[N << 1], f[172033][N], num[N << 1];
bool vis[N << 1];

struct node {
    int x, y;
    inline bool operator < (const node &b) const {
        return buc[this->x][this->y] < buc[b.x][b.y];
    }
    inline bool operator == (const node &b) const {
        return this->x == b.x && this->y == b.y;
    }
} a[N + N];

void prep(const int u) {
    vis[u] = 1;
    if (u <= n)
        ++tmp_x;
    else
        ++tmp_y;
    for (int i = 1; i <= mp[u][0]; ++i)
        if (!vis[mp[u][i]])
            prep(mp[u][i]);
}

int main() {
    //freopen("in", "r", stdin);
    freopen("factory.in", "r", stdin);
    freopen("factory.out", "w", stdout);
    n = in();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) {
            char c = getchar();
            while (c < '0' || c > '1')
                c = getchar();
            if (c == '1') {
                mp[i][++mp[i][0]] = j + n;
                mp[j + n][++mp[j + n][0]] = i;
            }
            init_edge += c == '1';
        }
    for (int i = 1; i <= n + n; ++i)
        if (!vis[i]) {
            tmp_x = tmp_y = 0;
            prep(i);
            ++buc[tmp_x][tmp_y];
        }

    for (int i = 0; i <= n; ++i)
        for (int j = 0; j <= n; ++j)
            if (buc[i][j])
                a[++m] = (node){i, j};
    std::sort(a + 1, a + 1 + m);
    m = std::unique(a + 1, a + 1 + m) - a - 1;
    pre[1] = 1;
    for (int i = 2; i <= m + 1; ++i)
        pre[i] = pre[i - 1] * (buc[a[i - 1].x][a[i - 1].y] + 1);

    memset(f, inf, sizeof(f));
    f[0][0] = 0;

    int nowx, nowy;
    for (int s = 0; s < (pre[m + 1]); ++s) {
        nowx = nowy = 0;
        for (int i = 1; i <= m; ++i)
            num[i] = s % pre[i + 1] / pre[i];
        for (int i = 1; i <= m; ++i)
            nowx += num[i] * a[i].x, nowy += num[i] * a[i].y;
        if (nowx == nowy)
            for (int i = 0; i < nowx; ++i)
                chk_min(f[s][nowx], f[s][i] + (nowx - i) * (nowx - i));
        for (int i = 1; i <= m; ++i)
            if (num[i] < buc[a[i].x][a[i].y])
                for (int j = 0; j <= n; ++j)
                    chk_min(f[s + pre[i]][j], f[s][j]);
    }
    printf("%d\n", f[pre[m + 1] - 1][n] - init_edge);

    return 0;
}

Guess you like

Origin www.cnblogs.com/15owzLy1-yiylcy/p/11348880.html