zoukankan      html  css  js  c++  java
  • 「SDOI2019」世界地图(并查集+kruscal)

    Address

    loj3112

    luogu P5360

    bzoj5531

    Solution

    对于 \(1\leq i\leq m\),考虑分别预处理经度在 \([1,i]\)\([i,m]\) 的点的 \(\text{MST}\)。询问的时候合并 \([1,l-1]\)\([r+1,m]\) 即可。

    先考虑怎么预处理 \([1,i]\)\(\text{MST}\)\([i,m]\) 同理)。

    假设我们已经有了 \([1,i-1]\)\(\text{MST}\),现在要在这里加上经度为 \(i\) 的点和一些边。

    考虑加入一条边 \((u,v,w)\) 会发生什么:

    \(1.\) \(u,v\) 不连通,连接 \(u,v\)
    \(2.\) \(u,v\) 已经连通,且路径 \(u→v\) 的边的最大值 \(\leq w\),什么也不会发生。
    \(3.\) \(u,v\) 已经连通,且路径 \(u→v\) 的边的最大值 \(> w\),断开这条最大边,连接 \(u,v\)

    也就是说,\([1,i-1]\)\(\text{MST}\) 中可能会有一些边被删掉。我们把 \(\text{MST}\)第一列和最后一列的点称为关键点,那么被删掉的边 \(l\) 必定满足:存在关键点 \(x,y\) 使得 \(l\) 是路径 \(x→y\) 上的权值最大边。

    换句话说,把端点的经度在 \([1,i-1]\) 的边全部拿出来跑 \(\text{kruscal}\)。边 \(l\) 会连接两个连通块,如果这两个连通块里面都有关键点,那么 \(l\) 可能被删掉,否则 \(l\) 不可能被删掉。

    因此记录边集 \(pre_i\) 表示经度在 \([1,i]\) 的点的 \(\text{MST}\) 中,之后可能被删的边。

    \([1,i-1]\)\(\text{MST}\) 中,不在 \(pre_{i-1}\) 的边(之后肯定不会被删的边)肯定都在 \([1,i]\)\(\text{MST}\) 中。 假设 \(pre_{i-1}\) 中的边的端点都是关键点,那么我们只要把 \(pre_{i-1}\) 和新加入的 \(2n-1\) 条边一起拿出来跑 \(\text{kruscal}\),所得结果加上肯定不会被删的边,就是 \([1,i]\)\(\text{MST}\)

    可是 \(pre_{i-1}\) 的边的端点不一定都是关键点,怎么办呢?

    考虑 \(\text{kruscal}\) 的过程:

    int fu = find(u), fv = find(v);
    if (fu != fv) 在 MST 中加入边 (u, v, w);
    

    其实和这样是等价的:

    int fu = find(u), fv = find(v);
    if (fu != fv) 在 MST 中加入边 (fu, fv, w);
    

    假设 \(pre_{i-1}\) 中的边都是关键点,那么可以考虑这样求出 \(pre_i\)

    inline void solve(vector<edge> &a, vector<edge> &b, ll &del)
    {
        int len = a.size(), i;
        sort(a.begin(), a.end(), cmp);
        b.clear(); del = 0;
        for (i = 0; i < len; i++)
        {
            int x = a[i].x, y = a[i].y, v = a[i].v, fx = find(x), fy = find(y);
            if (fx == fy) del += v;
            else
            {
                if (bo[fx] && bo[fy]) link(fx, fy), b.push_back((edge){fx, fy, v}); 
                // 保证 b 中的边都是关键点
                else if (bo[fx]) link(fx, fy);
                else link(fy, fx);
            }
        }
    }
    

    其中 \(a\)\(pre_{i-1}\) 加上新的 \(2n-1\) 条边,\(bo_x=1\) 表示 \(x\) 是关键点,\(b\)\(pre_i\)

    这样我们就得到了 \(pre_i\)\(suf_i\) 同理。

    对于询问 \((l,r)\),只要把 \(pre_{l-1}\)\(suf_{r+1}\) 合并即可,方法跟已知 \(pre_{i-1}\)\(pre_i\) 差不多。

    时间复杂度 \(O(nm\log n)\)

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    
    template <class t>
    inline void read(t & res)
    {
        char ch;
        while (ch = getchar(), !isdigit(ch));
        res = ch ^ 48;
        while (ch = getchar(), isdigit(ch))
        res = res * 10 + (ch ^ 48);
    }
    
    template <class t>
    inline void print(t x)
    {
        if (x > 9) print(x / 10);
        putchar(x % 10 + 48);
    }
    
    const int N = 105, M = 10005, T = N * M;
    struct edge
    {
        int x, y, v;
    };
    vector<edge> pre[M], suf[M];
    int n, rht[N][M], dwn[N][M], m, f[T], lim, q;
    bool bo[T];
    unsigned int SA, SB, SC; 
    ll pres[M], sufs[M], ans; 
    // pres[i] 表示 [1,i] 的 MST 的边权之和,sufs[i] 同理
    
    inline int getweight() 
    {
        SA ^= SA << 16;
        SA ^= SA >> 5;
        SA ^= SA << 1;
        unsigned int t = SA;
        SA = SB;
        SB = SC;
        SC ^= t ^ SA;
        return SC % lim + 1;
    }
    
    inline void gen() 
    {
        read(n); read(m); read(SA); read(SB); read(SC); read(lim);
        int i, j;
        for (i = 1; i <= n; i++)
            for (j = 1; j <= m; j++) 
                rht[i][j] = getweight();
        for (i = 1; i < n; i++)
            for (j = 1; j <= m; j++) 
                dwn[i][j] = getweight();
    }
    
    inline int id(int x, int y)
    {
        return (x - 1) * m + y;
    }
    
    inline int find(int x)
    {
        return f[x] == x ? x : f[x] = find(f[x]);
    }
    
    inline bool cmp(const edge &a, const edge &b)
    {
        return a.v < b.v;
    }
    
    inline void link(int x, int y)
    {
        f[y] = x;
    }
    
    inline void upt(int x, bool y)
    {
        f[x] = x;
        bo[x] = y;
    }
    
    inline void solve(vector<edge> &a, vector<edge> &b, ll &del)
    {
        int len = a.size(), i;
        sort(a.begin(), a.end(), cmp);
        b.clear(); del = 0;
        for (i = 0; i < len; i++)
        {
            int x = a[i].x, y = a[i].y, v = a[i].v, fx = find(x), fy = find(y);
            if (fx == fy) del += v;
            else
            {
                if (bo[fx] && bo[fy]) link(fx, fy), b.push_back((edge){fx, fy, v});
                else if (bo[fx]) link(fx, fy);
                else link(fy, fx);
            }
        }
    }
    
    inline void init_pre()
    {
        int i, j;
        for (i = 1; i <= n * m; i++) f[i] = i;
        ll del;
        vector<edge> a, b;
        for (i = 1; i <= m; i++)
        {
            for (j = 1; j <= n; j++)
            {
                upt(id(j, i), 1);
                upt(id(j, 1), 1);
                if (i > 2) upt(id(j, i - 1), 0);
            }
            a = pre[i - 1];
            pres[i] = pres[i - 1];
            for (j = 1; j <= n; j++)
            {
                if (i != 1)
                {
                    a.push_back((edge){id(j, i - 1), id(j, i), rht[j][i - 1]});
                    pres[i] += rht[j][i - 1];
                } 
                if (j != n)
                {
                    a.push_back((edge){id(j, i), id(j + 1, i), dwn[j][i]});
                    pres[i] += dwn[j][i];
                }
            }
            solve(a, b, del);
            pre[i] = b;
            pres[i] -= del;
        }
    }
    
    inline void init_suf()
    {
        int i, j;
        ll del;
        for (i = 1; i <= n * m; i++) f[i] = i, bo[i] = 0;
        vector<edge> a, b;
        for (i = m; i >= 1; i--)
        {
            for (j = 1; j <= n; j++)
            {
                upt(id(j, i), 1);
                upt(id(j, m), 1);
                if (i < m - 1) upt(id(j, i + 1), 0);
            }
            a = suf[i + 1];
            sufs[i] = sufs[i + 1];
            for (j = 1; j <= n; j++)
            {
                if (i != m)
                {
                    a.push_back((edge){id(j, i + 1), id(j, i), rht[j][i]});
                    sufs[i] += rht[j][i];
                } 
                if (j != n)
                {
                    a.push_back((edge){id(j, i), id(j + 1, i), dwn[j][i]});
                    sufs[i] += dwn[j][i];
                }
            }
            solve(a, b, del);
            suf[i] = b;
            sufs[i] -= del;
        }
    }
    
    int main()
    {
        gen();
        init_pre();
        init_suf();
        int l, r, i;
        read(q);
        for (i = 1; i <= n * m; i++) f[i] = i, bo[i] = 0;
        while (q--)
        {
            read(l); read(r);
            ans = pres[l - 1] + sufs[r + 1];
            vector<edge> a, b;
            for (i = 1; i <= n; i++)
            {
                ans += rht[i][m];
                a.push_back((edge){id(i, m), id(i, 1), rht[i][m]});
                upt(id(i, m), 0);
                upt(id(i, 1), 0);
                upt(id(i, l - 1), 0);
                upt(id(i, r + 1), 0);
            }
            int len = pre[l - 1].size();
            for (i = 0; i < len; i++) a.push_back(pre[l - 1][i]);
            len = suf[r + 1].size();
            for (i = 0; i < len; i++) a.push_back(suf[r + 1][i]);
            ll del = 0;
            solve(a, b, del);
            ans -= del;
            print(ans); putchar('\n');
        }
        return 0;
    }
    
    
  • 相关阅读:
    <经验杂谈>C#/.Net中xml的Serialization序列化与DeSerializetion反序列化
    <经验杂谈>C#中一种最简单、最基本的反射(Reflection):通过反射获取方法函数
    应用highcharts做直观数据统计
    ASP.net获取当前页面的文件名,参数,域名等方法
    C#中唯一标识符GUID的一些知识点
    理清fineuploader无刷新上传的一些事
    webform开发经验(一):Asp.Net获取Checkbox选中的值
    C#/.Net Post获取数据流的一种简单写法
    C#中的一种按日期分文件夹的日志写法
    C# Datatable导出Excel方法
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196019.html
Copyright © 2011-2022 走看看