• [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 eq 0) 一样

    • (S eq 0)

      (ans = lceilfrac{|S|}{m} ceil)

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

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

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

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

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

      好像不太好搞,考虑暴力

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

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

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

    复杂度:(O(mlceil frac{|S|}{m} ceil) = 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", Dinic());
        return 0;
    }
    
    
  • 相关阅读:
    VS2013连接SQLSERVER数据库时显示无法添加数据连接
    线段树模板
    网格中的极大子矩形的另类解法
    斜率优化
    三维前缀和
    Math Magic ZOJ
    01背包 多重背包 完全背包模板记录
    多重背包的单调队列优化
    Largest Rectangle in a Histogram POJ
    Game with string CodeForces
  • 原文地址:https://www.cnblogs.com/zzy2005/p/10406638.html
Copyright © 2020-2023  润新知