HNOI2013解题报告

8 篇文章 0 订阅
6 篇文章 0 订阅

HNOI2013解题报告

Author: Pengyihao

Day1 T1 比赛


思路

这是一道搜索的题目。

一个重要的优化就是,因为球队的分数排序后是不影响后面的答案的,所以判重的时候可以很方便。

然后还有就是 2810 是不会爆 long long 的……


代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(), f = 1; x = 0;
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}

const int MAXN = 20, MOD = 1000000007;

LL a[MAXN];

std::map<LL, LL>mp;

LL hash(int now)
{
    LL res = now, tmp[MAXN];
    FOR(i, 1, now) tmp[i] = a[i];
    std::sort(tmp + 1, tmp + now + 1, std::less<int>());
    FOR(i, 1, now) res += res * 28 + tmp[i];
    return res;
}

LL DFS(int now, int n)
{
    if (a[n] > 3 * (n - now)) return -1;

    LL res = 0;

    if (now == n) {
        if (n == 1) return 1;
        else {
            LL h = hash(n - 1);
            if (mp[h]) return mp[h];
            return mp[h] = DFS(1, n - 1);
        }
    }

    if (a[n] >= 3) {
        LL tmp = 0;
        a[n] -= 3, tmp = DFS(now + 1, n);
        if (tmp != -1) (res += tmp) %= MOD;
        a[n] += 3;
    }

    if (a[n] && a[now]) {
        LL tmp = 0;
        a[n]--, a[now]--, tmp = DFS(now + 1, n);
        if (tmp != -1) (res += tmp) %= MOD;
        a[n]++, a[now]++;
    }

    if (a[now] >= 3) {
        LL tmp = 0;
        a[now] -= 3, tmp = DFS(now + 1, n);
        if (tmp != -1) (res += tmp) %= MOD;
        a[now] += 3;
    }

    return res ? res : -1;
}

int n;

int main()
{
    freopen("match.in", "r", stdin);
    freopen("match.out", "w", stdout);

    in(n);

    FOR(i, 1, n) in(a[i]);

    std::sort(a + 1, a + n + 1, std::less<int>());

    printf("%lld\n", DFS(1, n));

    return 0;
}

Day1 T2 消毒


思路

可以发现最优的话一定要是,选择的区域中的一维为 1 ,另外两维为最大值。

因为 abc5000,所以它们中的最小值是小于 20 的。

把这一维看作高,然后枚举每一层选不选。

然后剩下的就转化成了二维问题。

这就是个典型的二分图最小点覆盖,网络流跑就可以了。

时间复杂度为 O( ) 。注意要进行常数优化。


代码

#include <bits/stdc++.h>

typedef long long LL;

#define debug(...) fprintf(stderr, __VA_ARGS__)

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(), f = 1; x = 0;
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}

using std::vector;

const int MAXN = 5002;

bool chose[20];

int a[4], ans;

bool map[MAXN][MAXN];

std::vector<std::vector<int> >g[MAXN];

void init()
{
    ans = 0x3f3f3f3f;

    FOR(i, 1, 3) in(a[i]);

    FOR(i, 0, a[1] + 1) {
        g[i].resize(a[2] + 2);
        FOR(j, 0, a[2] + 1) g[i][j].resize(a[3] + 2);
    }

    FOR(i, 1, a[1]) {
        FOR(j, 1, a[2]) FOR(k, 1, a[3]) in(g[i][j][k]);
    }
}

const int ss = 0, tt = 5008, INF = 0x3f3f3f3f;

using std::queue;

queue<int>q;
bool link[MAXN];
int dis[5010], cur[5010];
int cnt, head[5010], data[20010], flow[20010], nxt[20010];

void add(int x, int y, int z)
{
    nxt[cnt] = head[x]; data[cnt] = y; flow[cnt] = z; head[x] = cnt++;
    nxt[cnt] = head[y]; data[cnt] = x; flow[cnt] = 0; head[y] = cnt++;
}

int times, maxx = 1, alledge;

struct Edge {
    int x, y;
    Edge(int a=0, int b=0): x(a), y(b) {}
};

std::vector<Edge>edge[20];

bool bfs()
{
    dis[ss] = times; q.push(ss);
    while (!q.empty()) {
        int now = q.front(); q.pop();
        for (int i = head[now]; i != -1; i = nxt[i]) {
            if (dis[data[i]] < times && flow[i]) {
                dis[data[i]] = dis[now] + 1;
                q.push(data[i]);
                chkmax(maxx, dis[now] + 1);
            }
        }
    }
    return dis[tt] >= times;
}

int dfs(int now, int fl)
{
    if (now == tt) return fl;
    int flo;
    for (int i = head[now]; i != -1; i = nxt[i]) {
        if (flow[i] && dis[data[i]] == dis[now] + 1) {
            if (flo = dfs(data[i], Min(fl, flow[i]))) {
                flow[i] -= flo;
                flow[i ^ 1] += flo;
                return flo;
            }
        }
    }
    return 0;
}

void check(int minx)
{
    int tmptot = 0;
    FOR(i, 1, minx) if (chose[i]) tmptot++;

    if (tmptot >= ans) return;

    cnt = 0;

    int tot = tmptot;

// ***********************************************

// make_Edge

    int Addition;

    if (minx == a[1]) Addition = a[2];
    else if (minx == a[2]) Addition = a[1];
    else Addition = a[1];

    FOR(k, 1, minx) if (!chose[k]) {
        FOR(i, 0, edge[k].size() - 1) {
            int fr = edge[k][i].x, to = edge[k][i].y + Addition;
            if (!map[fr][to - Addition]) {
                add(fr, to, 1);
                if (!link[fr]) {link[fr] = true; add(ss, fr, 1);}
                if (!link[to]) {link[to] = true; add(to, tt, 1);}
            }
        }
    }



// ***********************************************

    int fl = 0;

    times = maxx + 1;

    while (bfs()) {
        //memcpy(cur, head, sizeof head);
        int tmp;
        while (tmp = dfs(ss, INF)) fl += tmp;

        times = maxx + 1;
    }

    chkmin(ans, fl + tot);

    FOR(k, 1, minx) if (!chose[k]) FOR(i, 0, edge[k].size() - 1) {
        map[edge[k][i].x][edge[k][i].y] = false;
        link[edge[k][i].x] = link[edge[k][i].y + Addition] = false;
        head[edge[k][i].x] = head[edge[k][i].y + Addition] = -1;
    }

    head[ss] = head[tt] = -1;
}

void search(int now, int minx)
{
    chose[now] = false;
    if (now == minx) check(minx);
    else search(now + 1, minx);
    chose[now] = true;
    if (now == minx) check(minx);
    else search(now + 1, minx);
    chose[now] = false;
}

void work()
{
    memset(head, -1, sizeof head);

    int minx = 5000;
    FOR(i, 1, 3) chkmin(minx, a[i]);

    alledge = 0;

    FOR(i, 1, minx) {
        edge[i].clear();
    }

    if (minx == a[1]) {
        FOR(k, 1, a[1]) {
            FOR(i, 1, a[2])    FOR(j, 1, a[3])
                if (g[k][i][j]) edge[k].push_back(Edge(i, j));
        }
    }

    else if (minx == a[2]) {

            FOR(i, 1, a[1]) FOR(k, 1, a[2]) { FOR(j, 1, a[3])
                if (g[i][k][j]) edge[k].push_back(Edge(i, j));
        }
    }

    else {

            FOR(i, 1, a[1]) FOR(j, 1, a[2]) FOR(k, 1, a[3]) {
                if (g[i][j][k]) edge[k].push_back(Edge(i, j));
            }
    }

    search(1, minx);
    printf("%d\n", ans);
    debug("%d\n", ans);
}

int main()
{
    freopen("clear.in", "r", stdin);
    freopen("clear.out", "w", stdout);

    int tcase; in(tcase);

    while (tcase--) {
        init();
        work();
    }

    return 0;
}

Day1 T3 旅行

这是一道很难的题目。我看了题解也暂时无法解决此题,所以决定跳过它。


Day2 T1 数列


思路

考虑差分数列。

差分数列的可能数量为 mk1

于是答案就为 nk1i=1ai

其中 ai 表示差分数列的第 i 项,最前面的 表示枚举的差分数列。

n 可以提出来,而 ai 的贡献是独立的。

所以最后答案为

mk1×nm(m+1)/2×(k1)×mk2


代码
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(), f = 1; x = 0;
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}

LL ans = 0;
LL n, k, m, p;

LL power(LL x, LL y, LL p)
{
    x %= p;

    LL ret = 1;
    while (y) {
        if (y & 1) ret = 1ll * ret * x % p;
        x = 1ll * x * x % p;
        y >>= 1;
    }
    return ret;
}

int main()
{
    freopen("seq.in", "r", stdin);
    freopen("seq.out", "w", stdout);

    in(n); in(k); in(m); in(p);
    n %= p;

    ans = 1ll * power(m, k - 1, p) * n % p;
    ans = ((ans - 1ll * (1ll * m * (m + 1) / 2) % p *
           (k - 1) % p * power(m, k - 2, p) % p) % p + p) % p;
    printf("%lld\n", ans);

    return 0;
}

Day2 T2 游走


思路

我们可以贪心来做——求出每条边走过的期望次数,然后从大到小分配从 1 m 的编号。

边的期望次数的求法是一个裸的高斯消元,这里就不再赘述。


代码

#include <bits/stdc++.h>

const int MAXN = 510;

bool f[MAXN][MAXN];
int n, m, du[MAXN], all;
long double matrix[MAXN][MAXN], times[MAXN], timess[MAXN * MAXN];

template <typename Tp> void in(Tp &x) {
    char ch = getchar(); x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}

void gauss() {
    for (int j = 1; j <= n; j++) {
        long double maxu = -1;
        int maxv = 0;
        for (int i = j; i <= n + 1; i++)
            if (maxu < fabs(matrix[i][j])) {
                maxu = fabs(matrix[i][j]);
                maxv = i;
            }
        for (int i = 1; i <= n + 1; i++) {
            long double t = matrix[j][i];
            matrix[j][i] = matrix[maxv][i];
            matrix[maxv][i] = t;
        }
        long double eps = matrix[j][j];
        if (fabs(eps) < 1e-10) continue;
        for (int i = 1; i <= n + 1; i++)
            matrix[j][i] /= eps;
        for (int i = 1; i <= n + 1; i++)
            if (i != j) {
                long double epss = matrix[i][j];
                if (fabs(epss) < 1e-10) continue;
                for (int k = 1; k <= n + 1; k++)
                    matrix[i][k] -= matrix[j][k] * epss;
            }
    }
    for (int i = 1; i <= n; i++)
        times[i] = matrix[i][n + 1] / matrix[i][i];
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
            if (f[i][j]) {
                if (j == n) timess[++all] = times[i] / du[i];
                else timess[++all] = times[i] / du[i] + times[j] / du[j];
            }
    std::sort(timess + 1, timess + all + 1);
    long double ans = 0;
    for (int i = 1; i <= all; i++) {
        ans += timess[i] * (all - i + 1);
    }
    printf("%.3Lf\n", ans);
}

int main() {
    freopen("walk.in", "r", stdin);
    freopen("walk.out", "w", stdout);
    in(n); in(m);
    for (int i = 1; i <= m; i++) {
        int u, v;
        in(u); in(v);
        f[u][v] = f[v][u] = true;
        if (u != n) du[u]++;
        if (v != n) du[v]++;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < n; j++)
            if (f[i][j])
                matrix[i][j] = (long double)(1) / du[j];
        matrix[i][i] = -1;
        if (i == 1) matrix[i][n + 1] = -1;
    }
    matrix[n + 1][n] = 1;
    matrix[n + 1][n + 1] = 1;
    gauss();
    return 0;
}

Day2 T3 切糕


思路

对于每一个位置,从所有层向上一层连边,容量为点权。

对于每一个位置,从第 i 层向相邻位置的 id 层连边,容量为 +

对于每一个位置,从源点向第一层连边,容量为 +

对于每一个位置,从最高层向汇点连边,容量为 +


代码
#include <bits/stdc++.h>

#define PLA(i, j, k) ((i) * 2500 + (j) * 50 + (k))

typedef long long LL;

#define FOR(i, a, b) for (int i = (a), i##_END_ = (b); i <= i##_END_; i++)
#define DNF(i, a, b) for (int i = (a), i##_END_ = (b); i >= i##_END_; i--)

template <typename Tp> void in(Tp &x) {
    char ch = getchar(), f = 1; x = 0;
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

template <typename Tp> Tp chkmax(Tp &x, Tp y) {return x > y ? x : x=y;}
template <typename Tp> Tp chkmin(Tp &x, Tp y) {return x < y ? x : x=y;}
template <typename Tp> Tp Max(Tp x, Tp y) {return x > y ? x : y;}
template <typename Tp> Tp Min(Tp x, Tp y) {return x < y ? x : y;}

int p, qs, r, d;

int height[50][50][50];

int cnt, dis[200010], head[200010], data[200010 << 1], flow[200010 << 1], cur[200010], nxt[200010 << 1];

using std::queue; queue<int>q;

const int ss = 0, tt = 200009, INF = 0x3f3f3f3f;

bool bfs()
{
    memset(dis, -1, sizeof dis);
    dis[ss] = 0; q.push(ss);
    while (!q.empty()) {
        int now = q.front(); q.pop();
        for (int i = head[now]; i != -1; i = nxt[i]) {
            if (dis[data[i]] == -1 && flow[i]) {
                dis[data[i]] = dis[now] + 1;
                q.push(data[i]);
            }
        }
    }
    return dis[tt] != -1;
}

int dfs(int now, int fl)
{
    if (now == tt) return fl;
    int flo;
    for (int &i = cur[now]; i != -1; i = nxt[i]) {
        if (flow[i] && dis[data[i]] == dis[now] + 1) {
            if (flo = dfs(data[i], Min(fl, flow[i]))) {
                flow[i] -= flo;
                flow[i ^ 1] += flo;
                return flo;
            }
        }
    }
    return 0;
}

void add(int x, int y, int z)
{
    nxt[cnt] = head[x]; data[cnt] = y; flow[cnt] = z; head[x] = cnt++;
    nxt[cnt] = head[y]; data[cnt] = x; flow[cnt] = 0; head[y] = cnt++;
}

int main()
{
    freopen("cake.in", "r", stdin);
    freopen("cake.out", "w", stdout);

    memset(head, -1, sizeof head);

    in(p); in(qs); in(r); in(d);

    FOR(i, 1, r) {
        FOR(j, 1, p) FOR(k, 1, qs) in(height[i][j][k]);
    }

    FOR(i, 1, p) FOR(j, 1, qs) {
        add(ss, PLA(i, j, 1), INF);
        FOR(k, 1, r) {
            add(PLA(i, j, k), PLA(i, j, k + 1), height[k][i][j]);
        }
        add(PLA(i, j, r + 1), tt, INF);
    }

    FOR(i, 1, p) FOR(j, 1, qs) {
        FOR(k, d + 1, r + 1) {
            if (i != p) add(PLA(i, j, k), PLA(i + 1, j, k - d), INF);
            if (i != 1) add(PLA(i, j, k), PLA(i - 1, j, k - d), INF);
            if (j != qs) add(PLA(i, j, k), PLA(i, j + 1, k - d), INF);
            if (j != 1) add(PLA(i, j, k), PLA(i, j - 1, k - d), INF);
        }
    }

    int fl = 0;

    while (bfs()) {
        memcpy(cur, head, sizeof head);
        int tmp; while (tmp = dfs(ss, INF)) fl += tmp;
    }

    printf("%d\n", fl);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值