[luogu5360] [SDOI2019R2 d1t3] 世界地图 - 图论 - 最小生成树 - kruskal重构树 - 并查集

传送门:https://www.luogu.org/problemnew/show/P5360

题目大意:给定网格图,左边界和右边界相连,每次询问挖掉一个横坐标区间之后其余部分的mst。

先%一下现场切掉这题的ckw队长。

每次询问可以看作把一个前缀和一个后缀拼起来做mst,因此我们可以先预处理出每个前缀和后缀的一些信息,再想办法把两棵mst拼成一棵。

最简单的想法就是用lct维护动态mst。假设我们已经得到了询问的前缀和后缀的mst,现在在中间加一堆边,如果之前没连通就直接连上,否则踢掉环上边权最大的边。

只可惜每次询问拎出一个前缀和后缀的mst可能需要可持久化lct,我也不知道有没有这个东西但我可以肯定的是没人会。

或者用树状数组套lct,每次把log个区间的lct拼起来,刚刚想了想这确实可以,但是……2个log+巨大的常数,不t才怪好吧。

那怎么办呢?我们考查一下用lct维护动态mst的过程。

把左右两棵mst拼起来,这个过程中哪些边可能被踢掉呢?

由于每次加边都只涉及最边缘的一排点,最终被踢掉的边当然也只会是原mst上连接最边缘一排点的边。

更直观的说法是:建出kruskal重构树(叶子节点是原先的点,每次加入一条边连接两个连通块时就新建一个点作为这两个连通块的father),则我们只需要考虑所有边缘的点的虚树上涉及到的边。

也就是对所有的关键点两两求一下lca(lca一定是新建节点,别忘了每个新建节点代表一条边),所有的lca代表的边就是我们要找的关键边。

如果我们已经知道每个前缀和后缀的这些关键边都是哪些,就可以每次合并时,拎出两侧的关键边以及中间的边来跑一次kruskal。

现在的问题就是:每个前缀和后缀的关键边怎么维护?

以前缀为例,我们要对每个前缀都维护出最左一排点的关键边。

从左到右扫过去,每次加一列的过程其实本质上和刚才的查询答案是一样的,还是合并左右两段的mst。

因此我们也要顺便维护出最右侧一排点的关键边。

左右两排点共2n个,我们要一起维护这个点集的关键边,一共有2n-1条。

每次加入一排点,把之前的关键边拿出来,再加上新加入的2n-1条边一起跑kruskal,然后再提取出新的关键边。

提取新的关键边的过程当然可以显式地建出kruskal重构树,当然更简单的办法是直接在并查集合并时就维护出来,调整一下merge时的合并顺序即可。

总复杂度O(nmlogn),这个log来自对边进行排序,据说实现得好可以把log去掉。

以及为什么泥萌的代码都辣么短啊……献上我5k的长代码:

#include<bits/stdc++.h>
using namespace std;
#define gc getchar()
#define pc putchar
#define li long long
inline li read(){
    li x = 0,y = 0,c = gc;
    while(!isdigit(c)) y = c,c = gc;
    while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ '0'),c = gc;
    return y == '-' ? -x : x;
}
inline void print(li q){
    if(q < 0) pc('-'),q = -q;
    if(q >= 10) print(q / 10);
    pc(q % 10 + '0');
}
int n,m,mo,q;
unsigned int sa,sb,sc;
int nxt(){
    sa ^= sa << 16;
    sa ^= sa >> 5;
    sa ^= sa << 1;
    unsigned int t = sa;
    sa = sb;
    sb = sc;
    sc ^= t ^ sa;
    return sc % mo + 1;
}
struct edge{
    int u,v;
    li w;
    edge(int _u = 0,int _v = 0,li _w = 0){u = _u;v = _v;w = _w;}
}e1[10010][110],e2[10010][110],e3[10010][110],e4[10010][110],nw[1010],tmp[1010];
inline bool operator < (edge q,edge w){
    return q.w < w.w;
}
int f[1000010],bh[110][10010],cnt;
inline int getf(int q){
    return f[q] == q ? q : f[q] = getf(f[q]);
}
int tp1,tp2;
inline void mg(int u,int v){
    if(getf(u) % m == tp1) f[getf(v)] = getf(u);
    else if(getf(v) % m == tp1) f[getf(u)] = getf(v);
    else if(getf(u) % m == tp2) f[getf(v)] = getf(u);
    else f[getf(u)] = getf(v);
}
li an1[10010],an2[10010],an3[10010],an4[10010];
int main(){
    int i,j,u,v,w,tt1,tt2;
    n = read();m = read();sa = read();sb = read();sc = read();mo = read();
    for(i = 1;i <= n;++i) for(j = 1;j <= m;++j) bh[i][j] = (i - 1) * m + j;
    for(i = 1;i <= n;++i) for(j = 1;j <= m;++j){
        w = nxt();
        if(j < m) e1[j][i] = edge(bh[i][j],bh[i][j + 1],w);
        else e1[j][i] = edge(bh[i][j],bh[i][1],w);
    }
    for(i = 1;i < n;++i) for(j = 1;j <= m;++j){
        w = nxt();
        e2[j][i] = edge(bh[i][j],bh[i + 1][j],w);
    }
    for(i = 1;i <= m;++i) sort(e1[i] + 1,e1[i] + n + 1),sort(e2[i] + 1,e2[i] + n);

    for(i = 1;i < n;++i) e3[1][i] = e2[1][i];
    cnt = tt1 = tt2 = 0;tp1 = 1;tp2 = 2;
    for(i = 1;i <= n;++i) f[bh[i][1]] = bh[i][1],f[bh[i][2]] = bh[i][2];
    for(i = 1;i < n;++i) nw[++cnt] = e2[1][i];
    for(i = 1;i < n;++i) nw[++cnt] = e2[2][i];
    for(i = 1;i <= n;++i) nw[++cnt] = e1[1][i];
    sort(nw + 1,nw + cnt + 1);
    for(i = 1;i <= cnt;++i) if(getf(nw[i].u) != getf(nw[i].v)){
        tmp[++tt1] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
        if(getf(nw[i].u) % m == 1 && getf(nw[i].v) % m == 1) e3[2][++tt2] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
        else an1[2] += nw[i].w;
        mg(nw[i].u,nw[i].v);
    }
    for(j = 3;j <= m;++j){
        an1[j] = an3[j] = an3[j - 1];
        cnt = tt1 = tt2 = 0;tp1 = 1;tp2 = j;
        for(i = 1;i <= n;++i) f[bh[i][1]] = bh[i][1],f[bh[i][j]] = bh[i][j],f[bh[i][j - 1]] = bh[i][j - 1];
        for(i = 1;i < 2 * n;++i) nw[++cnt] = tmp[i];
        for(i = 1;i < n;++i) nw[++cnt] = e2[j][i];
        for(i = 1;i <= n;++i) nw[++cnt] = e1[j - 1][i];
        sort(nw + 1,nw + cnt + 1);
        for(i = 1;i <= cnt;++i) if(getf(nw[i].u) != getf(nw[i].v)){
            if(getf(nw[i].u) % m != j - 1 && getf(nw[i].v) % m != j - 1) tmp[++tt1] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
            else an3[j] += nw[i].w;
            if(getf(nw[i].u) % m == 1 && getf(nw[i].v) % m == 1) e3[j][++tt2] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
            else an1[j] += nw[i].w;
            mg(nw[i].u,nw[i].v);
        }
    }

    for(i = 1;i < n;++i) e4[m][i] = e2[m][i];
    cnt = tt1 = tt2 = 0;tp1 = 0;tp2 = m - 1;
    for(i = 1;i <= n;++i) f[bh[i][m]] = bh[i][m],f[bh[i][m - 1]] = bh[i][m - 1];
    for(i = 1;i < n;++i) nw[++cnt] = e2[m][i];
    for(i = 1;i < n;++i) nw[++cnt] = e2[m - 1][i];
    for(i = 1;i <= n;++i) nw[++cnt] = e1[m - 1][i];
    sort(nw + 1,nw + cnt + 1);
    for(i = 1;i <= cnt;++i) if(getf(nw[i].u) != getf(nw[i].v)){
        tmp[++tt1] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
        if(getf(nw[i].u) % m == 0 && getf(nw[i].v) % m == 0) e4[m - 1][++tt2] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
        else an2[m - 1] += nw[i].w;
        mg(nw[i].u,nw[i].v);
    }
    for(j = m - 2;j;--j){
        an2[j] = an4[j] = an4[j + 1];
        cnt = tt1 = tt2 = 0;tp1 = 0;tp2 = j;
        for(i = 1;i <= n;++i) f[bh[i][m]] = bh[i][m],f[bh[i][j]] = bh[i][j],f[bh[i][j + 1]] = bh[i][j + 1];
        for(i = 1;i < 2 * n;++i) nw[++cnt] = tmp[i];
        for(i = 1;i < n;++i) nw[++cnt] = e2[j][i];
        for(i = 1;i <= n;++i) nw[++cnt] = e1[j][i];
        sort(nw + 1,nw + cnt + 1);
        for(i = 1;i <= cnt;++i) if(getf(nw[i].u) != getf(nw[i].v)){
            if(getf(nw[i].u) % m != j + 1 && getf(nw[i].v) % m != j + 1) tmp[++tt1] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
            else an4[j] += nw[i].w;
            if(getf(nw[i].u) % m == 0 && getf(nw[i].v) % m == 0) e4[j][++tt2] = edge(getf(nw[i].u),getf(nw[i].v),nw[i].w);
            else an2[j] += nw[i].w;
            mg(nw[i].u,nw[i].v);
        }   
    }

    q = read();
    for(i = 1;i <= q;++i){
        u = read();v = read();cnt = 0;
        for(j = 1;j <= n;++j) f[bh[j][1]] = bh[j][1],f[bh[j][m]] = bh[j][m];
        for(j = 1;j < n;++j) nw[++cnt] = e3[u - 1][j];
        for(j = 1;j < n;++j) nw[++cnt] = e4[v + 1][j];
        for(j = 1;j <= n;++j) nw[++cnt] = e1[m][j];
        sort(nw + 1,nw + cnt + 1);
        li as = an1[u - 1] + an2[v + 1];
        for(j = 1;j <= cnt;++j) if(getf(nw[j].u) != getf(nw[j].v)){
            mg(nw[j].u,nw[j].v);
            as += nw[j].w;
        }
        print(as);pc('\n');
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值