「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;
}

原文地址:https://www.cnblogs.com/cyf32768/p/12196019.html

时间: 2024-10-26 17:18:58

「SDOI2019」世界地图(并查集+kruscal)的相关文章

Loj #3111. 「SDOI2019」染色

Loj #3111. 「SDOI2019」染色 题目描述 给定 \(2 \times n\) 的格点图.其中一些结点有着已知的颜色,其余的结点还没有被染色.一个合法的染色方案不允许相邻结点有相同的染色. 现在一共有 \(c\) 种不同的颜色,依次记为 \(1\) 到 \(c\).请问有多少对未染色结点的合法染色方案? 输入格式 第一行有两个整数 \(n\) 和 \(c\),分别描述了格点图的大小和总的颜色个数. 之后两行,每行有 \(n\) 个整数:如果是 \(0\) 则表示对应结点未被染色,否

「并查集」程序自动分析

程序自动分析 原题链接:程序自动分析 题目大意 给你很多逻辑关系,判断是否有冲突 题目题解 先将两个逻辑分开来看,前一个逻辑存在那么后一个逻辑一定不存在,如果存在那么答案就错误了,可以用并查集维护,详细见代码 //#define fre yes #include <cstdio> #include <iostream> #include <algorithm> const int N = 1000005; struct message { int x, y, e; }

「带权并查集」奇偶游戏

奇偶游戏 原题链接:奇偶游戏 题目大意 给你N个区间,每个区间给你它含1的奇偶性,问你哪些询问逻辑上冲突 题目题解 一道带权并查集的题,让我对带权并查集有了更深入的理解,带权并查集可以分为两种(在这道题中) "边带权"并查集 "扩展域"并查集 两种方法都是思维上的不同所造成的,其中第一种解法是最常见的,第二种解法在代码实现上是最简单的,我们先来对第一种进行探究 边带权,很明显,我们要在并查集的边上进行一个储存边权的操作,我们这里用d来表示当前节点到根节点的Xor值,

「UVA 11987」Almost Union-Find 「带权并查集」「思维」

你发现,这个第二个操作不可能用普通并查集来搞,很棘手 但是你考虑一下,并查集维护的是个森林结构,并且路径压缩的时候每个森林的根是不会变的, 也就是意味着每删掉一个点你需要让他的踪影消失匿迹即可,并不需要让他在原有的树结构上消失. 具体怎么消失?把贡献全在根上减掉即可,再新建一个新点连进去. 这个新点可以用id数组表示,即id[x]为x节点现在的编号. #include <bits/stdc++.h> #define test(...) fprintf(stderr, __VA_ARGS__)

P1546 最短网络(kruscal+并查集)

从邻接矩阵中提取出边,然后跑一边kruscal 1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 struct node 5 { 6 int x,y,w; 7 }; 8 int cnt=0;//记录边数 9 node a[100*100];//记录边 10 int fa[110];//记录并查集 11 int finds(int x)//并查集找祖先 12 { 13 while(fa[x]!=x)

并查集:Union-Find(1)

Disjoint Sets: 我们都知道Sets(集合)是什么,就是一组非重复元素组成的结构. 先让我们来看一下Disjoint Sets(非相交集合) : Disjoint Sets的意思是一堆集合们,它们相互之间都没有交集.没有交集是指:各个集合之间没有拥有共同.相同的元素.中文称作「分离集」. Disjoint Sets 的性质相当特殊.信息学家仔细观察其特性后,精心设计出一套优雅美观的资料结构,可以快速的做集合运算. 由于每个 Disjoint Sets 指的就是集合们都没有交集,我们就

大數據的「真面目」及其運用

大數據的定義 近年來,人們對「大數據」的關注度日益提高.這都歸因於麥肯錫全球研究院在2011年發布的研究報告.該報告認為人們即將迎來一個利用規模大到超出現有數據處理系統能力的巨量信息時代,並暗示戰略性地利用這些信息數據,就有可能產生巨大的商業機會. 那麼大數據到底是什麼呢?從字面來看,它指的是以現有信息處理技術無法應對的龐大信息量.而實際上,當我們將儲蓄了各種服務的使用信息數據與用戶的屬性信息相結合,並在這些信息數據發生時能夠全量獲取,就被稱做大數據. 典型的是互聯網服務的利用數據.另外還包括零

「SCOI2016」萌萌哒

「SCOI2016」萌萌哒 题目描述 一个长度为 \(n\) 的大数,用 \(S_1S_2S_3 \ldots S_n\) 表示,其中 \(S_i\) 表示数的第 \(i\) 位,\(S_1\) 是数的最高位,告诉你一些限制条件,每个条件表示为四个数 $(l_1, r_1, l_2, r_2) $,即两个长度相同的区间,表示子串 $S_{l_1}S_{l_1 + 1}S_{l_1 + 2} \ldots S_{r_1} $与 \(S_{l_2}S_{l_2 + 1}S_{l_2 + 2} \ld

「题解」kuangbin 最小生成树

POJ-1251 Jungle Roads (水题,%c) POJ-1287 Networking (水) POJ-2031 Building a Space Station (%f浮点数尴尬精度,两球间距离) POJ-2421 Constructing Roads (一些边已建好,简单处理一下) ZOJ-1586 QS Network (处理一下边权) HDU-1233 还是畅通工程 (水) HDU-1875 畅通工程再续 (浮点数,条件连边) HDU-1301 Jungle Roads (重