首页 > 其他分享 >P5227 [AHOI2013] 连通图

P5227 [AHOI2013] 连通图

时间:2023-11-04 15:34:33浏览次数:25  
标签:连通 cut 启用 int vl pp AHOI2013 link P5227

P5227 [AHOI2013] 连通图

(膜拜并感谢 @Genius_Z 给予本题解思路)

因为这一题是线段树合并板题,所以我们使用 LCT。

考虑最暴力的想法,维护一棵树和很多不在树上的边,每一次询问就暴力拆边,从那些没有被禁的边里面补到树上。

这个时候我们就会发现,每次 “补边” 的操作非常的消耗时间。

于是我们就可以想到利用一些 LCT 的性质优化一下。观察题目的本质,其实就是把每一个边按照询问的时间分成很多段 “启用区间” ,那我们 “补边” 的本质又是什么呢?

假设我们断了一条边 a,启用了另一条边 b,那么就说明当前的询问时间 超过了 a 目前的启用区间,而并没有超过 b 的启用区间。那么只要我使树上的边的启用区间右端点尽量的大,是不是就不需要补边了?

考虑对边额外标定一些边权 \(vl_i\),表示下一次被禁用的询问时间,也就是启用区间的右端点 +1,然后跑最大生成树,每次询问就断边,修改预处理出来的新边权,再加边即可。

至此,我们就还剩下一个问题:我不会维护虚子树的 size。

这其实也非常好解决,只需要在涉及虚实儿子更改的地方(access 操作、link 操作)统计一下虚儿子的 size 之和。当然还有重中之重,为了 cut 操作的方便,我们需要在其中多split一下,确保两个点是实儿子与父亲的关系,不然我们不知道到底是实儿子还是虚儿子。

现在,我们可以用 LCT 切了这道题霸气离场,然后回家偷偷补线段树分治的知识了。

代码 3K,不多
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10,inf=1e9+10;
int tg[N],s[N][2],f[N],st[N],top,n,m,k,vl[N],sz[N],xu[N],a[N],a1,a2;
void up(int x){
    sz[x]=sz[s[x][0]]+sz[s[x][1]]+xu[x]+(x<=n);
    if(x>n) vl[x]=x;
    else vl[x]=0;
    if(s[x][0]&&a[vl[s[x][0]]]<a[vl[x]]) vl[x]=vl[s[x][0]];
    if(s[x][1]&&a[vl[s[x][1]]]<a[vl[x]]) vl[x]=vl[s[x][1]];
}
void rev(int x){swap(s[x][0],s[x][1]),tg[x]^=1;}
void down(int x){if(tg[x]) rev(s[x][0]),rev(s[x][1]);tg[x]=0;}
int get(int x){return s[f[x]][1]==x;}
int nrt(int x){return s[f[x]][0]==x||s[f[x]][1]==x;}
void rot(int x){
    int y=f[x],z=f[y],o=get(x);
    if(nrt(y)) s[z][get(y)]=x;
    f[x]=z;
    s[y][o]=s[x][o^1],f[s[x][o^1]]=y;
    s[x][o^1]=y,f[y]=x;
    up(y),up(x);
}
void upd(int x){
    st[top=1]=x;
    while(nrt(x)) st[++top]=x=f[x];
    while(top) down(st[top--]);
}
void splay(int x){
    upd(x);
    for(int y;y=f[x],nrt(x);rot(x)) if(nrt(y)) rot(get(x)^get(y)?x:y);
    up(x);
}
void access(int x){for(int y=0;x;x=f[y=x]) splay(x),xu[x]+=sz[s[x][1]],s[x][1]=y,xu[x]-=sz[y],up(x);}
void make(int x){access(x),splay(x),rev(x);}
int find(int x){
    access(x),splay(x),down(x);
    while(s[x][0]) down(x=s[x][0]);
    splay(x);
    return x;
}
void split(int x,int y){make(x),access(y),splay(y);}
void link(int x,int y){
    make(y);
    f[y]=x,xu[x]+=sz[y],up(x); 
}
void cut(int x,int y){
    split(y,x);
    s[x][0]=f[y]=0,up(x);
}
struct node{
    int x,y,z,p;
}u,kk[N];
vector<node>d[N];
int nxt[2][N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&u.x,&u.y),u.z=i+n,kk[i]=u;
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        int oo,uu,v;
        scanf("%d",&oo);
        for(int j=1;j<=oo;j++){
            scanf("%d",&uu),d[i].push_back(kk[uu]),v=kk[uu].z;
            if(nxt[0][v]) d[nxt[0][v]][nxt[1][v]].p=i;
            else a[v]=i;
            nxt[0][v]=i,nxt[1][v]=j-1;
        } 
    }
    for(int i=n+1;i<=n+m;i++){
        if(nxt[0][i]) d[nxt[0][i]][nxt[1][i]].p=inf;
        if(!a[i]) a[i]=inf;
    }
    for(int i=0;i<=n;i++) a[i]=inf;
    for(int i=1;i<=m;i++){
        node j=kk[i];
        make(j.x);
        if(find(j.y)==j.x){
            split(j.y,j.x);
            int pp=vl[j.x];
            if(a[pp]>=a[j.z]) continue;
            cut(pp,kk[pp-n].x),cut(pp,kk[pp-n].y);
        }
        link(j.z,j.x),link(j.z,j.y);
    }
    for(int i=1;i<=k;i++){
        for(node j:d[i]){
            make(j.x);
            if(find(j.y)==j.x){
                up(j.z);
                if(!f[j.z]&&!sz[j.z]) continue;
                cut(j.z,j.x),cut(j.z,j.y);
            }
        }
        access(1),splay(1);
        if(sz[1]==n) puts("Connected");
        else puts("Disconnected");
        for(node j:d[i]){
            make(j.x),a[j.z]=j.p;
            if(find(j.y)==j.x){
                split(j.y,j.x);
                int pp=vl[j.x];
                if(a[pp]>=j.p) continue;
                cut(pp,kk[pp-n].x),cut(pp,kk[pp-n].y);
            }
            link(j.z,j.x),link(j.z,j.y);
        }
    }
}

标签:连通,cut,启用,int,vl,pp,AHOI2013,link,P5227
From: https://www.cnblogs.com/2020ljh/p/17809391.html

相关文章

  • 2023-11-01:用go语言,沿街有一排连续的房屋。每间房屋内都藏有一定的现金, 现在有一位小
    2023-11-01:用go语言,沿街有一排连续的房屋。每间房屋内都藏有一定的现金,现在有一位小偷计划从这些房屋中窃取现金,由于相邻的房屋装有相互连通的防盗系统,所以小偷不会窃取相邻的房屋,小偷的窃取能力定义为他在窃取过程中能从单间房屋中窃取的最大金额,给你一个整数数组nums......
  • 2023-11-01:用go语言,沿街有一排连续的房屋。每间房屋内都藏有一定的现金, 现在有一位小
    2023-11-01:用go语言,沿街有一排连续的房屋。每间房屋内都藏有一定的现金,现在有一位小偷计划从这些房屋中窃取现金,由于相邻的房屋装有相互连通的防盗系统,所以小偷不会窃取相邻的房屋,小偷的窃取能力定义为他在窃取过程中能从单间房屋中窃取的最大金额,给你一个整数数组nums表示每......
  • WGCLOUD体验 - 监测数据库的连通性
    WGCLOUD是一款运维监测平台,可以监测服务器、主机、数据库、服务接口、网络设备等资源我是一名DBA,日常工作中,主要关注数据库方面的监测情况,正好WGCLOUD有一个模块可以检测数据库是否能连通,如果发现不能连通,会立刻发送告警通知(可以用邮件、钉钉、wx等方式)如下WGCLOUD不但可以检测数据......
  • 双连通性
    参考资料:虞皓翔《再谈图连通性相关算法》......
  • 有向图转强连通图最少加边数
    原文链接问题描述对于一有向图,若需要保证任选一点即可走到其它所有点,询问最少需要加多少条有向边结论对于一有向图,若其对应DAG中入度为0的点数为$p$,出度为0的点数为$q$,则答案数为$max(p,q)$证明:$p\leqq$和$p\geqq$的证明过程类似,这里仅说明$p\leqq$的证明过程当$......
  • TARJAN复习 求强连通分量、割点、桥
    TARJAN复习求强连通分量、割点、桥目录TARJAN复习求强连通分量、割点、桥强连通分量缩点桥割点感觉之前写的不好,再水一篇博客强连通分量“有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(......
  • 当防火墙开通策略后如何验证端口服务已经连通了?
    当防火墙开通策略后如何验证端口服务已经连通了?假设策略开通的没有问题。在源主机上进行测试:1.Windows下测试TCP端口格式:telnet【目的IP/域名】端口telnetwww.baidu.com443成功则会显示以下界面 telnetwww.baidu.com135失败则会显示以下界面 2.在Linux(cent......
  • [学习笔记]强连通分量
    定义什么是强连通分量?直白地说就是在一个有向图中,有一块区域,每个点都可以互相抵达。这里用一张图来说明一下。图中的\(1,2,3\)是一个强连通分量,因为他们可以互相抵达。Tarjan算法如何求强连通分量,最有名且最常用的就是Tarjan算法。先给出如下定义:\(dfn_u\):深搜时被......
  • 树上的最大权连通块:一种换根动态规划与贪心算法的结合
    树上的最大权连通块:一种换根动态规划与贪心算法的结合在计算机科学中,树是一种非常特殊的数据结构,不仅因为它们在存储数据时的效率,还因为它们提供了一种非常直观且强大的方式来解决各种问题。今天,我们将探讨一种特殊类型的问题,即在一棵树中找到一个特殊的子集或连通块,该子集中的节......
  • 连通性与 Tarjan
    强连通分量和Tarjan强连通分量(StronglyConnectedComponents,SCC),指的是极大的连通子图。tarjan算法,用于求图的强连通分量。dfs生成树有向图的dfs生成树大致分为四种边:树边(treeedge):示意图中以黑色边表示,每次搜索找到一个还没有访问过的结点的时候就形成了一条树边......