[HNOI2013]题解

代码在最后

[HNOI2013]比赛

记忆化搜索

把每一位还需要多少分用\(27\)进制压进\(long\) \(long\),\(map\)记忆化一下即可

[HNOI2013]消毒

先考虑在二维平面

问题就是最小点覆盖

最小点覆盖 = 二分图最大匹配

对于每个点\((x,y)\),\(x\)连向\(y\)一条边,然后跑最大匹配

扩展到三维

好像不太好搞....

\(a*b*c<=5000\)

我们把\(a\)调换成\(min(a, b, c)\)

\(a\)最大为\(\sqrt[3]{5000}\)\(17\)

对于每一层,要么和上面一起消,要么一次消掉这一层

枚举每层的状态

然后再跑最大匹配\(O(2^{17}n^3)\)

[HNOI2013]旅行

构造+单调队列

题目大意:给定\(1,-1\)组成的序列,分成\(m\)段,使得\(m\)段绝对值最大值最小

设:

后缀和为\(sum[i]\),后缀和为\(0\)的个数为\(cnt[i]\),序列和为\(S\)

然后分类讨论

  • \(S = 0\)

    \(1.\)\(cnt[1] \ge m\),则\(ans = 0\).直接选\(0\)的位置即可

    维护一个单调队列

    \(2.\)\(cnt[1]<m\), 则\(ans = 1\)

    构造方法与 \(S \neq 0\) 一样

  • \(S \neq 0\)

    \(ans = \lceil\frac{|S|}{m}\rceil\)

    因为区间和为\(0\)的可以消掉,剩下的均分最小

    现在问题是如何求出字典序最小的方案

    假设上一个休息点已经确定为\(last\),当前决策到第\(i\)个休息点

    那么新的休息点(第i个休息点)$$a$需要满足:

    • \(n - a \ge m-i\)
    • \(\lceil \frac{|sum[a + 1]|}{m - i}\rceil \leq ans\)
    • \(|sum[last+1]-sum[a+1]| \leq ans\)

    好像不太好搞,考虑暴力

    我们可以对于每个每种\(sum\)开一个单调队列

    每次暴力从\([sum[last+1]-ans, sum[last+1]+ans]\) 取出合法的放进答案队列

    再维护答案队列的单调性,保证答案字典序最小

复杂度:\(O(m\lceil \frac{|S|}{m} \rceil) = O(|S|)\)

[HNOI2013]数列

\(20\%\):\(dp\)
\(f[i][j]\)\(i\)位,已经分了\(j\)
转移用前缀和优化一下
\(O(nk)\)

\(100\%\):

先写出式子:
设差分数组为:\(a_1, a_2, a_3, ..., a_{k-1}\)

\(\sum^{m}_{a_1 = 1}\sum^{m}_{a_2 = 1}\sum^{m}_{a_3 = 1}...\sum^{m}_{a_{k-1} = 1}(n-\sum_{i=1}^{k-1}a_i)\)
\(= n * m^{k-1} - \sum^{m}_{a_1 = 1}\sum^{m}_{a_2 = 1}\sum^{m}_{a_3 = 1}...\sum^{m}_{a_{k-1} = 1}\sum_{i=1}^{k-1}a_i\)
\(=n*m^{k-1}-\sum_{i=1}^{k-1}\sum_{a_i=1}^m*m^{k-2}\)
\(=n*m^{k-1}-(k-1)*m^{k-2}*\frac{(m+1)m}{2}\)

\(快速幂即可\)

  • 注意取模

[HNOI2013]游走

题面要求算边的期望

如果求出每条边期望经过次数,就可以求出答案

每条边经过次数,显然需要求出两端点期望经过次数

设点\(i\)期望次数为\(f[i]\)

\(deg[i]\)\(i\)的入度

对于一个点\(i\)

存在边\(x-i\)

\(f[i] = \sum \frac{f[x]}{deg[x]}\)

\(f[1]\)要加\(1\),因为一开始在\(1\)

\(f[n]\)不要算,因为到了\(n\)就停止

这个东西列个方程,高斯消元求

然后算出每条边经过次数的期望
对于\(x-y\)
\(q[i] = f[x] / deg[x] + f[y] / deg[y]\)

显然经过次数多的边,赋值小

排序弄一下即可

[HNOI2013]切糕

最小割

题目其实就是求最小的代价使得每个纵轴被分成两部分

我们把每个点抽象成一条边,一个纵轴就是一条\(S−T\)的路径

但是题目要求\(|f(x,y)−f(x′,y′)|≤D\)
不能直接跑最小割

考虑如何限制

首先,\(|f(x,y)−f(x′,y′)|≤D\)是相互的
所以只要考虑 \(f(x,y)−f(x′,y′)≤D\)

那么对于\((x, y, z)\) 我们向它相邻列的第\(x-D\)层连一条\(inf\)

贪心想一下,这样一定保证超过限制的两个点不会同时割掉

然后代码就很简单啦

Code

[HNOI2013]比赛

#include<bits/stdc++.h>

#define LL unsigned long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 15, Mod = 1e9 + 7;

int n, s[N], ans, a[N], b[N], cnt1, cnt2;

map<LL, LL> M;

bool cmp(int x, int y) {
    return x > y;
}

LL dfs(int x, int y) {
    if (a[x] + (n - y + 1) * 3 < s[x]) return 0;
    if (x == n)	return 1;
    if (y > n) {
        for (int i = x + 1; i <= n; i++) b[i] = s[i] - a[i];
        sort(b + 1 + x, b + n + 1, cmp);
        LL S = n - x;
        for (int i = x + 1; i <= n; i++) S = S * 27 + b[i];
        if (M.find(S) != M.end()) return M[S];
        else return M[S] = dfs(x + 1, x + 2);
    }
    LL res = 0;

    if (a[x] + 3 <= s[x] && cnt1) {
        a[x] += 3;
        cnt1--;
        (res += dfs(x, y + 1)) %= Mod;
        a[x] -= 3;
        cnt1++;
    }
    if (a[x] < s[x] && a[y] < s[y] && cnt2) {
        a[x]++; a[y]++;
        cnt2--;
        (res += dfs(x, y + 1)) %= Mod;
        a[x]--; a[y]--;
        cnt2++;
    }
    if (a[y] + 3 <= s[y] && cnt1) {
        a[y] += 3;
        cnt1--;
        (res += dfs(x, y + 1)) %= Mod;
        a[y] -= 3;
        cnt1++;
    }
    return res;
}

int main() {
    read(n);
    int sum;
    for (int i = 1; i <= n; i++)
        read(s[i]), sum += s[i];
    sort(s+1, s+1+n, cmp);
    cnt1 = sum - n * (n - 1);
    cnt2 = (sum - cnt1 * 3) / 2;
    printf("%lld\n", dfs(1, 2));
    return 0;
}

[HNOI2013]消毒

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 5010;

int a, b, c;

int q[3][N], ql, ans;

void init() {
    ql = 0;
    read(a), read(b), read(c);
    int mn = min(a, min(b, c));
    for (int i = 1; i <= a; i++)
        for (int j = 1; j <= b; j++)
            for (int k = 1; k <= c; k++) {
                int x; read(x);
                if (x == 1)
                    q[0][++ql] = i, q[1][ql] = j, q[2][ql] = k;
            }
    if (mn == b) {
        swap(a, b); swap(q[0], q[1]);
    }
    else if (mn == c) {
        swap(a, c); swap(q[0], q[2]);
    }
    return ;
}

bool flag[N], used[N];

int match[N];

struct node {
    int to, nxt;
}g[N];
int last[N], gl;

void add(int x, int y) {
    g[++gl] = (node) {y, last[x]};
    last[x] = gl;
}

bool dfs(int u) {
    for (int i = last[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (used[v]) continue;
        used[v] = 1;
        if (!match[v] || dfs(match[v])) {
            match[v] = u;
            return 1;
        }
    }
    return 0;
}

void work(int S) {
    int res = 0;
    for (int i = 0; i < a; i++)
        if (S & (1 << i))
            flag[i + 1] = 1, res++;
        else flag[i + 1] = 0;
    memset(last, 0, sizeof(last)); gl = 0;
    memset(match, 0, sizeof(match));
    for (int i = 1; i <= ql; i++)
        if (!flag[q[0][i]])
            add(q[1][i], q[2][i]);
    for (int i = 1; i <= b; i++) {
        for (int j = 1; j <= c; j++) used[j] = 0;
        if (dfs(i)) res++;
    }
    ans = min(ans, res);
    return ;
}

int main() {
    int T;
    read(T);
    while (T--) {
        init();
        ans = 2147483647;
        for (int i = 0; i < (1 << a); i++)
            work(i);
        printf("%d\n", ans);
    }
    return 0;
}

[HNOI2013]旅行

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}
const int N = 5e5 + 5; 
int n, m, a[N], sum[N], cnt[N];
struct Node { int l, r, v; } Line[N << 1]; int tot = 0; 
struct deque { 
    int head, tail, len;
    bool empty() { return !len; } 
    int newNode(int l, int r, int v) { Line[++tot] = (Node){ l, r, v }; return tot; }
    int front() { return Line[head].v; } 
    int back() { return Line[tail].v; } 
    void pop_back() { tail = Line[tail].l, len--; } 
    void pop_front() { head = Line[head].r, len--; } 
    void push_back(int v) { 
        if (!len)  head = tail = newNode(0, 0, v); 
        else Line[tail].r = newNode(tail, 0, v), tail = Line[tail].r; 
        len++; 
    } 
    void push(int v) { 
        while (len && a[back()] > a[v]) pop_back(); 
        push_back(v);
    } 
} Q[N << 1], Qu[N << 1], *q = Q + N, *qu = Qu + N;

#define min(x, y) ((a[x]) < (a[y]) ? (x) : (y))

int main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++)
        read(a[i]), read(sum[i]), sum[i] = sum[i] ? 1 : -1;
    for (int i = n - 1; i >= 1; i--) sum[i] += sum[i + 1];
    for (int i = n; i >= 1; i--) cnt[i] = cnt[i + 1] + (!sum[i]);
    int S = sum[1], d = S ? ceil(1.0 * abs(S) / m) : cnt[1] < m;
    cnt[n + 1] = -1;
    if (!d) {
        for (int i = 1, j = 2; i < m; i++) {
            while (cnt[j + 1] >= m - i) {
                if (!sum[j + 1]) q[0].push(j);
                j++;
            }
            printf("%d ", a[q[0].front()]);
            q[0].pop_front();
        }
    }
    else {
        for (int i = 2; i <= n; i++) qu[sum[i]].push_back(i - 1);
        int last = 0;
        a[n + 1] = n + 1;
        for (int i = 1; i < m; i++) {
            int ans = n + 1;
            for (int j = sum[last + 1] - d; j <= sum[last + 1] + d; j++) {
                if (ceil(1.0 * abs(j) / (m - i)) > d) continue;
                while (!qu[j].empty() && n - qu[j].front() >= m - i) {
                    if (qu[j].front() > last) q[j].push(qu[j].front());
                    qu[j].pop_front();
                }
                while (!q[j].empty() && q[j].front() <= last)
                    q[j].pop_front();
                if (!q[j].empty()) ans = min(ans, q[j].front());
            }
            last = ans;
            printf("%d ", a[ans]);
        }
    }
    printf("%d\n", a[n]);
    return 0;
}

[HNOI2013]数列

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

LL n, k, m, p;

LL fastpow(LL a, LL b) {
    LL res = 1;
    for (; b; b >>= 1, a = a * a % p) if (b & 1) res = res * a % p; 
    return res;
}

int main() {
    //freopen(".in", "r", stdin);
    //freopen(".out", "w", stdout);
    read(n), read(k), read(m), read(p);
    LL ans = (n % p * fastpow(m, k - 1) % p - (k - 1) * fastpow(m, k - 2) % p * ((m + 1) * m / 2 % p) % p  + p) % p;
    write(ans);
    return 0;
}

[HNOI2013]游走

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 510;

struct node {
    int to, nxt;
}g[N * 1000];
int last[N], gl, deg[N], n, m;
void add(int x, int y) {
    g[++gl] = (node) {y, last[x]};
    last[x] = gl;
    g[++gl] = (node) {x, last[y]};
    last[y] = gl;
    deg[y]++; deg[x]++;
}

double a[N][N];

void Gauss() {
    for (int i = 1, k; i < n; i++) {
        k = i;
        for (int j = i + 1; j < n; j++)
            if (fabs(a[k][i]) < fabs(a[j][i])) k = j;
        if (k != i) swap(a[k], a[i]);
        for (int j = i + 1; j < n; j++)
            for (int k = n; k >= i; k--)
                a[j][k] -= a[i][k] * a[j][i] / a[i][i];
    }
    for (int i = n - 1; i; i--) {
        for (int j = i + 1; j < n; j++)
            a[i][n] -= a[i][j] * a[j][n];
        a[i][n] /= a[i][i];
    }
    return ;
}

double q[N * N];

int X[N * N], Y[N * N];

int main() {
    read(n); read(m);
    for (int i = 1, x, y; i <= m; i++) {
        read(x), read(y);
        add(x, y);
        X[i] = x, Y[i] = y;
    }	
    for (int x = 1; x < n; x++) {
        a[x][x] = 1;
        for (int i = last[x]; i; i = g[i].nxt) {
            int v = g[i].to;
            if (v == n) continue;
            a[x][v] = -1.0 / deg[v];
        }
    }
    a[1][n] = 1;
    Gauss();
    for (int i = 1; i <= m; i++)
        q[i] = a[X[i]][n] / deg[X[i]] + a[Y[i]][n] / deg[Y[i]];
    sort(q + 1, q + 1 + m);
    double ans = 0;
    for (int i = 1; i <= m; i++)
        ans += q[i] * (m - i + 1);
    printf("%.3lf\n", ans);
    return 0;
}

[HNOI2013]切糕

#include<bits/stdc++.h>

#define LL long long
#define RG register

using namespace std;
template<class T> inline void read(T &x) {
    x = 0; RG char c = getchar(); bool f = 0;
    while (c != '-' && (c < '0' || c > '9')) c = getchar(); if (c == '-') c = getchar(), f = 1;
    while (c >= '0' && c <= '9') x = x*10+c-48, c = getchar();
    x = f ? -x : x;
    return ;
}
template<class T> inline void write(T x) {
    if (!x) {putchar(48);return ;}
    if (x < 0) x = -x, putchar('-');
    int len = -1, z[20]; while (x > 0) z[++len] = x%10, x /= 10;
    for (RG int i = len; i >= 0; i--) putchar(z[i]+48);return ;
}

const int N = 80000, inf = 2147483647;

struct node {
    int to, nxt, w;
}g[2000000];
int last[N], gl = 1;

void add(int x, int y, int z) {
    g[++gl] = (node) {y, last[x], z};
    last[x] = gl;
    g[++gl] = (node) {x, last[y], 0};
    last[y] = gl;
}

queue<int> q;
int dep[N], s, t, cur[N];

bool bfs() {
    memset(dep, 0, sizeof(dep));
    dep[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = last[u]; i; i = g[i].nxt) {
            int v = g[i].to;
            if (!dep[v] && g[i].w) {
                dep[v] = dep[u]+1;
                q.push(v);
            }
        }
    }
    return dep[t] == 0 ? 0 : 1;
}

int dfs(int u, int d) {
    if (u == t) return d;
    for (int &i = cur[u]; i; i = g[i].nxt) {
        int v = g[i].to;
        if (dep[v] == dep[u]+1 && g[i].w) {
            int di = dfs(v, min(d, g[i].w));
            if (di) {
                g[i].w -= di;
                g[i^1].w += di;
                return di;
            }
        }
    }
    return 0;
}

int Dinic() {
    int ans = 0;
    while (bfs()) {
        for (int i = 1; i <= t; i++) cur[i] = last[i];
        while (int d = dfs(s, inf)) ans += d;
    }
    return ans;
}

int a[50][50][50], id[50][50][50];

int fx[] = {0, 1, -1, 0};
int fy[] = {1, 0, 0, -1};

int main() {
    int p, q, r, d, tot = 0;
    read(p), read(q), read(r), read(d);
    for (int i = 1; i <= r; i++)
        for (int j = 1; j <= p; j++)
            for (int k = 1; k <= q; k++)
                read(a[i][j][k]), id[i][j][k] = ++tot;
    for (int j = 1; j <= p; j++)
        for (int k = 1; k <= q; k++)
            id[r+1][j][k] = ++tot;			
    s = tot+1, t = s+1;
    for (int i = 1; i <= p; i++)
        for (int j = 1; j <= q; j++)
            add(s, id[1][i][j], inf), add(id[r+1][i][j], t, inf);
    
    for (int k = 1; k <= r; k++)
        for (int i = 1; i <= p; i++)
            for (int j = 1; j <= q; j++)
                add(id[k][i][j], id[k+1][i][j], a[k][i][j]);
    
    for (int k = d+1; k <= r+1; k++)
        for (int i = 1; i <= p; i++)
            for (int j = 1; j <= q; j++) {
                for (int z = 0; z < 4; z++) {
                    int x = i + fx[z], y = j + fy[z];
                    if (x < 1 || y < 1 || x > p || y > q) continue;
                    add(id[k][i][j], id[k-d][x][y], inf);
                }
            }
    
    printf("%d\n", Dinic());
    return 0;
}

posted @ 2019-02-20 14:57  zzy2005  阅读(252)  评论(1编辑  收藏  举报