BZOJ

你。 提交于 2019-12-02 16:00:08

决定从头到尾干一波BZOJ!
可能会写没几题就停下吧,但还是想学学新姿势啦。

[BeiJing2006]狼抓兔子

即求 $(1, 1)$ 到 $(n, m)$ 的最小割。跑 dinic 即可。

#include <bits/stdc++.h>
using namespace std;
 
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}
 
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
struct E { int v, ne, f; } e[N * 7];
int head[N], cnt, n, m, iter[N], level[N];
 
inline void add(int u, int v, int f) {
    e[cnt].v = v; e[cnt].f = f; e[cnt].ne = head[u]; head[u] = cnt++;
    e[cnt].v = u; e[cnt].f = f; e[cnt].ne = head[v]; head[v] = cnt++;
}
 
bool bfs(int s, int t) {
    for (int i = 0; i <= t; i++) level[i] = -1, iter[i] = head[i];
    queue<int> que;
    que.push(s);
    level[s] = 0;
    while (!que.empty()) {
        int u = que.front(); que.pop();
        for (int i = head[u]; ~i; i = e[i].ne) {
            int v = e[i].v, f = e[i].f;
            if (level[v] < 0 && f) {
                level[v] = level[u] + 1;
                que.push(v);
            }
        }
    }
    return level[t] != -1;
}
 
int dfs(int u, int t, int f) {
    if (u == t || !f) return f;
    int flow = 0;
    for (int i = iter[u]; ~i; i = e[i].ne) {
        iter[u] = i;
        int v = e[i].v;
        if (level[v] == level[u] + 1 && e[i].f) {
            int w = dfs(v, t, min(f, e[i].f));
            if (!w) continue;
            e[i].f -= w, e[i^1].f += w;
            flow += w, f -= w;
            if (f <= 0) break; 
        }
    }
    return flow;
}
 
int main() {
    memset(head, -1, sizeof(head));
    n = read(), m = read();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < m; j++) {
            int f = read();
            add((i - 1) * m + j, (i - 1) * m + j + 1, f);  
        }
    }
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= m; j++) {
            int f = read();
            add((i - 1) * m + j, i * m + j, f);
        }
    }
    for (int i = 1; i < n; i++) {
        for (int j = 1; j < m; j++) {
            int f = read();
            add((i - 1) * m + j, i * m + j + 1, f);
        }
    }
    int ans = 0;
    int s = 1, t = n * m;
    for (; bfs(s, t); ans += dfs(s, t, INF));
    printf("%d\n", ans);
    return 0;
}
View Code


[FJOI2007]轮状病毒
生成树计数。
基尔霍夫矩阵为度数矩阵减去邻接矩阵。
无向图生成树计数为基尔霍夫矩阵的行列式
可得递推方程
$ans = 3 \times f(n - 1) - 2 \times f(n - 2) - 2$
$f(n) = 3 \times f(n - 1) - f(n - 2)$
加上高精度即可。
注意算行列式时多写几行容易看。

#include <bits/stdc++.h>
using namespace std;

struct Bigi {
    int a[600], len;
    Bigi() {
        memset(a, 0, sizeof(a));
        len = 1;
    }
    friend Bigi operator * (int x, Bigi b) {
        Bigi res;
        res.len = b.len + 10;
        for (int i = 1; i <= res.len; i++) {
            res.a[i] += b.a[i] * x;
            res.a[i + 1] += res.a[i] / 10;
            res.a[i] %= 10;
        }
        while (res.len > 1 && res.a[res.len] == 0) res.len--;
        return res;
    }
    friend Bigi operator - (Bigi x, Bigi y) {
        Bigi res;
        res.len = x.len;
        for (int i = 1; i <= res.len; i++) {
            res.a[i] = x.a[i] - y.a[i];
            while (res.a[i] < 0) {
                res.a[i] += 10;
                x.a[i + 1]--;
            }
        }
        while (res.len > 1 && res.a[res.len] == 0) res.len--;
        return res;
    }
    friend Bigi operator + (Bigi x, int y) {
        Bigi res = x;
        res.a[1] += y;
        for (int i = 1; i <= res.len; i++) {
            if (res.a[i] >= 10) {
                res.a[i] -= 10;
                res.a[i + 1]++;
            } else {
                break;
            }
        }
        while (res.a[res.len + 1]) res.len++;
        return res;
    }
    void print() {
        for (int i = len; i; i--)
            printf("%d", a[i]);
        puts("");
    }
} big[150], ans;

int main() {
    int n;
    scanf("%d", &n);
    big[1] = big[1] + 3;
    big[2] = big[2] + 8;
    if (n <= 2) {
        big[n].print();
        return 0;
    }
    for (int i = 3; i <= n; i++) 
        big[i] = 3 * big[i - 1] - big[i - 2];
    ans = 3 * big[n - 1] - 2 * big[n - 2];
    ans = ans + (-2);
    ans.print();
    return 0;
}
View Code


[ZJOI2006]物流运输
$cost[i][j]$ 表示第 $i$ 天到第 $j$ 天都走同一条路线时每天的最小花费,即为 $1$ 到 $m$ 的最短路。dijkstra即可。
然后 $dp[i]$ 表示到第 $i$ 天的最小花费
$dp[i] = min(dp[j] + cost[j + 1][i] * (i - j) + k)$

#include <bits/stdc++.h>
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;

const int INF = 10000000;
const int N = 110;
int cost[N][N], dp[N], n, m, k, e;
vector<pii> G[N];
bool ban[N][N], unable[N], done[N];
int dis[N];

inline void checkmin(int &a, int b) {
    if (a > b) a = b;
}

int dijkstra(int l, int r) {
    for (int i = 1; i <= m; i++) {
        unable[i] = 0;
        done[i] = 0;
        dis[i] = INF;
        for (int j = l; j <= r; j++)
            unable[i] |= ban[j][i];
    }
    if (unable[1] || unable[m]) return INF;
    priority_queue<pii, vector<pii>, greater<pii> > que;
    dis[1] = 0;
    que.push(pii(0, 1));
    while (!que.empty()) {
        auto pp = que.top(); que.pop();
        int u = pp.se;
        if (done[u]) continue;
        done[u] = 1;
        for (auto p: G[u]) {
            int v = p.se, c = p.fi;
            if (!unable[v] && dis[v] > dis[u] + c) {
                dis[v] = dis[u] + c;
                que.push(pii(dis[v], v));
            }
        }
    }
    return dis[m];
}

int main() {
    //freopen("in.txt", "r", stdin);
    scanf("%d%d%d%d", &n, &m, &k, &e);
    for (int u, v, c; e--; ) {
        scanf("%d%d%d", &u, &v, &c);
        G[u].push_back(pii(c, v));
        G[v].push_back(pii(c, u));
    }
    int q;
    scanf("%d", &q);
    for (int u, a, b; q--; ) {
        scanf("%d%d%d", &u, &a, &b);
        for (int i = a; i <= b; i++)
            ban[i][u] = 1;
    }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cost[i][j] = dijkstra(i, j);
    for (int i = 1; i <= n; i++) {
        dp[i] = cost[1][i] * i;
        for (int j = 1; j < i; j++)
            checkmin(dp[i], dp[j] + cost[j + 1][i] * (i - j) + k);
    }
    printf("%d\n", dp[n]);
    return 0;
}
View Code

 

[HNOI2008]明明的烦恼
prufer序列为无根树的一种数列。长度为 $n$ - $2$
prufer转无根树
将最小编号的叶子删去,prufer序列加入其父亲。重复至树只剩下两个节点。
无根树转prufer
取出prufer首元素,与待选点集中最小未出现在prufer序列中的点连边,并将该点在待选点集中删去,直至待选点集剩下两个节点,将这两个节点连边。待选点集初始为 $1$ ~ $n$。
一个节点在prufer序列中出现次数为该节点度数减一。
判断无解的情况:出现度数为 $0$ 的点,在prufer序列中出现次数超过 $n$ - $2$。
有解情况下,设 $cnt$ 为有度数要求的节点个数,$sum = \sum_{i = 1} ^{cnt}(d_i - 1)$。
那么答案为 $C_{n-2}^{sum} \times \dfrac{sum!}{\prod_{i=1}^{cnt}(d_i-1)!} \times (n-cnt)^{n-2-sum}$
化简得到$\dfrac{(n-2)!}{(n-sum-2)! \times \prod_{i=1}^{cnt}(d_i-1)!} \times (n-cnt)^{n-2-sum}$

#include <bits/stdc++.h>

const int N = 1007;
const int MOD = 10000;
int num[N];
int prime[N], tol, d[N], c[N];
bool vis[N];

void init() {
    for (int i = 2; i < N; i++) {
        if (!vis[i]) prime[++tol] = i;
        for (int j = 1; j <= tol && i * prime[j] < N; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}

void add(int x, int o) {
    for (int i = 1; i <= tol; i++) {
        while (x % prime[i] == 0)
            c[i] += o, x /= prime[i];
    }
}

int main() {
    init();
    int n;
    scanf("%d", &n);
    bool flag = 0;
    int sum = 0, cnt = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", d + i);
        if (!d[i] || d[i] > n - 1) flag = 1;
        if (d[i] != -1) sum += d[i] - 1, cnt++;
    }
    if (n == 1) {
        if (!d[1]) puts("1");
        else puts("0");
        return 0;
    }
    if (sum > n - 2 || flag) {
        puts("0");
        return 0;
    }
    for (int i = n - 2 - sum + 1; i <= n - 2; i++)
        add(i, 1);
    for (int i = 1; i <= n; i++) {
        if (d[i] > -1) {
            for (int j = 2; j < d[i]; j++)
                add(j, -1);
        }
    }
    int len = 0;
    num[++len] = 1;
    for (int i = 1; i <= n - 2 - sum; i++) {
        for (int j = 1; j <= len; j++) num[j] *= n - cnt;
        for (int j = 1; j <= len; j++) {
            if (num[j] >= MOD) {
                num[j + 1] += num[j] / MOD;
                num[j] %= MOD;
            }
        }
        while (num[len + 1]) {
            num[len + 1] += num[len] / MOD;
            num[len] %= MOD;
            len++;
        }
    }
    for (int i = 1; i <= tol; i++) {
        while (c[i]) {
            for (int j = 1; j <= len; j++) num[j] *= prime[i];
            for (int j = 1; j <= len; j++) {
                if (num[j] >= MOD) {
                    num[j + 1] += num[j] / MOD;
                    num[j] %= MOD;
                }
            }
            while (num[len + 1]) {
                num[len + 1] += num[len] / MOD;
                num[len] %= MOD;
                len++;
            }
            c[i]--;
        }
    }
    printf("%d", num[len]);
    for (int i = len - 1; i; i--) printf("%04d", num[i]);
    puts("");
    return 0;
}
View Code


[HNOI2008]水平可见直线
可见的直线为一下凸壳。
先按斜率和截距从小到大排序,再用单调栈判断交点的相对位置即可。

#include <bits/stdc++.h>

const int N = 5e4 + 7;
const double eps = 1e-7;

inline int dcmp(double x) {
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

struct P {
    double x, y;
    int id;
    inline bool operator < (const P &rhs) const {
        if (dcmp(x - rhs.x) == 0) return y < rhs.y;
        return x < rhs.x;
    }
} p[N];

int st[N], top;

inline double crossx(const P &a, const P &b) {
    return (a.y - b.y) / (b.x - a.x);
}

void ins(int id) {
    const P &cur = p[id];
    while (top) {
        if (dcmp(p[st[top]].x - cur.x) == 0) top--;
        else if (top > 1 && dcmp(crossx(p[st[top]], cur) - crossx(p[st[top]], p[st[top - 1]])) <= 0) top--;
        else break;
    }
    st[++top] = id;
}

int ans[N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lf%lf", &p[i].x, &p[i].y);
        p[i].id = i;
    }
    std::sort(p + 1, p + 1 + n);
    for (int i = 1; i <= n; i++)
        ins(i);
    for (int i = 1; i <= top; i++)
        ans[p[st[i]].id] = 1;
    for (int i = 1; i <= n; i++)
        if (ans[i])
            printf("%d ", i);
    return 0;
}
View Code

 

[HNOI2008]越狱
总方案数 - 相邻颜色均不同的方案数。

#include <bits/stdc++.h>
#define ll long long

const int MOD = 100003;

int qp(int a, ll b) {
    a %= MOD;
    int ans = 1;
    while (b) {
        if (b & 1) ans = 1LL * ans * a % MOD;
        a = 1LL * a * a % MOD;
        b >>= 1;
    }
    return ans;
}

int main() {
    int m;
    ll n;
    scanf("%d%lld", &m, &n);
    int ans = qp(m, n);
    ans = (ans - 1LL * m * qp(m - 1, n - 1) % MOD) % MOD;
    ans = (ans + MOD) % MOD;
    printf("%d\n", ans);
    return 0;
}
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!