zoukankan      html  css  js  c++  java
  • 「6月雅礼集训 2017 Day2」C

    【题目大意】

    有一棵n个点的完全二叉树,边权均为1,每个点有小鸟容量c[i]

    依次来了m只小鸟,第i只小鸟初始位置在pos[i]上,问来了x只小鸟的时候,怎样安排小鸟的路线可以使得小鸟移动的边权和最小,且每个点的小鸟个数不超过小鸟容量。
    n,m<=3*10^5

    【题解】

    一眼看过去费用流

    有两种方法

    1. 我们对每次来小鸟都新建二分图,S->小鸟容量剩余的点,小鸟容量不够的点->T,互相连边即可。

    复杂度O(n^3 + n * MinCostFlow)

    2. 我们对于整棵树建一张图,每次相当于多连了一条边,跑一边spfa增广即可。

    复杂度O(n * MinCostFlow)

    考场写第一种啊。。qwq

    那么考虑第二种,我们模拟spfa的增广过程,显然是选一条最短路来增广。

    由于完全二叉树,树高log,我们可以枚举这只小鸟要迁到的点和pos的LCA,然后顺便维护子树内到这个点的最小值即可。

    每次选出一条路,就把这条路上模拟退流、流边、流反向边等等操作

    然后就能过了,由于完全二叉树,操作都是log的。

    总复杂度O(nlogn)

    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    
    # define RG register
    # define ST static
    
    const int M = 3e5 + 10;
    const int mod = 998244353, inf = 1e9;
    
    int n, m, d[M], id[M], f[M][2], c[M];
    
    # define ls (x<<1)
    # define rs (x<<1|1)
    
    inline void gs(int x) {
        d[x] = inf;
        if(c[x]) d[x] = 0, id[x] = x;
        if(ls <= n && d[ls] + (f[ls][0] ? -1 : 1) < d[x]) d[x] = d[ls] + (f[ls][0] ? -1 : 1), id[x] = id[ls];
        if(rs <= n && d[rs] + (f[rs][0] ? -1 : 1) < d[x]) d[x] = d[rs] + (f[rs][0] ? -1 : 1), id[x] = id[rs]; 
    }
    
    int main() {
    //    freopen("C.in", "r", stdin);
    //    freopen("C.out", "w", stdout);
        cin >> n >> m;
        for (int i=1; i<=n; ++i) scanf("%d", &c[i]);
        for (int i=n; i; --i) gs(i); 
        ll ans = 0;
        for (int i=1, x; i<=m; ++i) {
            scanf("%d", &x);
            int cnt = 0, pmi = inf, pid = 0;
            for (int par=x; par; par>>=1) {
                if(d[par] + cnt < pmi) pmi = d[par] + cnt, pid = par;
                cnt += (f[par][1] ? -1 : 1);
            }
            ans += pmi;
    //        cout << "pid = " << pid << "  pmi = " << pmi << ", id = " << id[pid] << endl;
            c[id[pid]] --;
            for (int y=id[pid]; y!=pid; y>>=1) if(f[y][0]) --f[y][0]; else ++f[y][1];
            for (int y=x; y!=pid; y>>=1) if(f[y][1]) --f[y][1]; else ++f[y][0];
            for (int y=id[pid]; y!=pid; y>>=1) gs(y);
            for (int y=x; y!=pid; y>>=1) gs(y);
            for (int y=pid; y; y>>=1) gs(y);
            printf("%lld
    ", ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    vim高亮
    mengning
    4.4内核osal
    tmpvalgrind
    为什么引入协程
    alloc_call_show(转)
    TSAN
    如何查看哪些进程占用Buffer和Cache高(转)
    ASAN详解其他参考链接
    Linux系统与程序监控工具atop教程(转)
  • 原文地址:https://www.cnblogs.com/galaxies/p/20170618_c.html
Copyright © 2011-2022 走看看