zoukankan      html  css  js  c++  java
  • bzoj 2001: City 城市建设 cdq

    题目

    PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。

    题解

    经典的动态最小生成树问题。
    可以采用cdq分治的方式来解决。
    核心思想就是:

    1. 对于无论被修改的边修改成什么样都一定会被加入的非修改边进行缩点以减小数据范围。
    2. 对于无论被修改的边修改成什么样都一定不被加入的非修改边进行删除以减小数据范围。

    对于两种边的确定可以直接设被修改的边的边权为-inf或inf,然后跑Kruskal确定
    复杂度。。。
    他们说是(O(nlog^2n))的。。。
    反正跑的很快就是了。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
        x=0;static char ch;static bool flag;flag = false;
        while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
        while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    #define rg register int
    #define rep(i,a,b) for(rg i=(a);i<=(b);++i)
    #define per(i,a,b) for(rg i=(a);i>=(b);--i)
    const int maxn = 50010;
    const int maxm = 100010;
    const int inf = 0x3f3f3f3f;
    struct Edge{
        int u,v,w,id;
        bool friend operator < (const Edge &a,const Edge &b){
            return a.w < b.w;
        }
    }e[30][maxm],L[maxm],tmp[maxm];
    int Nn[30],Ne[30];
    int fa[maxn];
    inline int find(int x){
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    inline bool merge(int u,int v){
        int x = find(u);
        int y = find(v);
        if(x == y) return false;
        fa[x] = y;return true;
    }
    int val[maxm];
    struct Node{
        int k,w;
    }qer[maxm];
    bool vis[maxm];
    int tmp_n[maxn],tmp_m[maxm],map[maxm];
    ll ans[maxm];
    inline void step1(int &n,int &m,ll &res){
        int N = 0,M = 0;
        rep(i,1,n) fa[i] = i;
        rep(i,0,m-1) vis[i] = 0;
        sort(L,L+m);
        rep(i,0,m-1){
            if(merge(L[i].u,L[i].v) && L[i].w != -inf){
                res += L[i].w;
                vis[i] = true;
            }else tmp[M ++ ] = L[i];
        }
        rep(i,1,n) fa[i] = i;
        rep(i,0,m-1) if(vis[i]) merge(L[i].u,L[i].v);
        rep(i,1,n) if(find(i) == i) tmp_n[i] = ++ N;
        rep(i,1,n) tmp_n[i] = tmp_n[find(i)];
        rep(i,0,M-1){
            L[i] = tmp[i];
            map[L[i].id] = i;
            L[i].u = tmp_n[L[i].u];
            L[i].v = tmp_n[L[i].v];
        }
        n = N;m = M;
    }
    inline void step2(int &n,int &m){
        int M = 0;
        rep(i,1,n) fa[i] = i;
        sort(L,L+m);
        rep(i,0,m-1){
            if(merge(L[i].u,L[i].v) || L[i].w == inf){
                map[L[i].id] = M;
                L[M++] = L[i];
            }
        }m = M;
    }
    inline void solve(int l,int r,int cur,ll res){
        int n = Nn[cur],m = Ne[cur];
        if(l == r) val[qer[r].k] = qer[r].w;
        rep(i,0,m-1){
            e[cur][i].w = val[e[cur][i].id];
            L[i] = e[cur][i];
            map[L[i].id] = i;
        }
        if(l == r){
            rep(i,1,n) fa[i] = i;
            sort(L,L+m);
            rep(i,0,m-1){
                if(merge(L[i].u,L[i].v)) res += L[i].w;
            }
            ans[l] = res;
            return ;
        }
        rep(i,l,r) L[map[qer[i].k]].w = -inf;step1(n,m,res);
        rep(i,l,r) L[map[qer[i].k]].w = inf;step2(n,m);
        Nn[cur+1] = n;Ne[cur+1] = m;
        rep(i,0,m-1) e[cur+1][i] = L[i];
        int mid = l+r >> 1;
        solve(l,mid,cur+1,res);
        solve(mid+1,r,cur+1,res);
    }
    int main(){
        int n,m,Q;read(n);read(m);read(Q);
        rep(i,0,m-1){
            read(e[0][i].u);
            read(e[0][i].v);
            read(e[0][i].w);
            val[i] = e[0][i].w;
            e[0][i].id = i;
        }
        rep(i,1,Q){
            read(qer[i].k);read(qer[i].w);
            -- qer[i].k;
        }
        Nn[0] = n;Ne[0] = m;
        solve(1,Q,0,0);
        rep(i,1,Q) printf("%lld
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    Ruby 集合数组常用遍历方法
    Git,Github和Gitlab简介和基本使用
    L1-Day14
    学习进度(2)
    求数组的子数组的最大值(文件存储)
    开学第一课博客——自我介绍
    求数组的子数组的最大值
    学习进度(1)
    java web+模板
    android开发环境配置以及测试所遇到的的问题
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6815611.html
Copyright © 2011-2022 走看看