[bzoj3514][CodeChef GERALD07] Chef ans Graph Queries [LCT+主席树]

题面

bzoj上的强制在线版本

思路

首先可以确定,这类联通块相关的询问问题,都可以$LCT$+可持久化记录解决

用LCT维护生成树作为算法基础

具体而言,从前往后按照边的编号顺序扫一遍边

如果这条边两端不在同一个$LCT$联通块中,则$link$

否则$cut$掉当前连接两条边的路径上的编号最小的边,并$link$

记录$ntr[i]$表示第$i$条边触发第二种情况时$link$前$cut$掉的边的编号

如果触发第一种情况,则$ntr[i]=0$

如果为自环,则$ntr[i]=i$

这样记录之后,建立$ntr[i]$的主席树,每次在$ntr[i]$的位置+1

对于查询$[l,r]$,$Ans=n-query(0,l-1,root[l-1],root[r])$

具体原因为:考虑每个$ntr$值大于等于$l$的边,显然它们在不在对答案都没有贡献(因为已经被ntr了)

否则,它们会连接两个联通块,但是这条边不在$[l,r]$区间内,所以有联通块个数-1的贡献

Code

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define mp make_pair
using namespace std;
inline int read(){
    int re=0,flag=1;char ch=getchar();
        while(!isdigit(ch)){
        if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
int fa[400010],ch[400010][2],val[400010],minn[400010],rev[400010];
void update(int x){
    minn[x]=x;
    if(val[minn[x]]>val[minn[ch[x][0]]]) minn[x]=minn[ch[x][0]];
    if(val[minn[x]]>val[minn[ch[x][1]]]) minn[x]=minn[ch[x][1]];
}
int nroot(int x){return ((ch[fa[x]][0]==x)||(ch[fa[x]][1]==x));}
int get(int x){return ch[fa[x]][1]==x;}
void pushrev(int x){
    if(!x) return;
    swap(ch[x][0],ch[x][1]);
    rev[x]^=1;
}
void pushdown(int x){
    if(!rev[x]) return;
    pushrev(ch[x][0]);
    pushrev(ch[x][1]);
    rev[x]=0;
}
void push(int x){
    if(!x) return;
    if(nroot(x)) push(fa[x]);
    pushdown(x);
}
void rotate(int x){
    int f=fa[x],ff=fa[f],son=get(x),nr=nroot(f);
//  cout<<"     rotate "<<x<<' '<<f<<' '<<ff<<' '<<son<<' '<<ch[x][0]<<' '<<ch[x][1]<<' '<<ch[f][0]<<' '<<ch[f][1]<<'\n';
    ch[f][son]=ch[x][son^1];
    if(ch[f][son]) fa[ch[f][son]]=f;
    fa[f]=x;ch[x][son^1]=f;
    fa[x]=ff;
    if(nr) ch[ff][ch[ff][1]==f]=x;
    update(f);update(x);
}
void splay(int x){
//  cout<<" splay "<<x<<'\n';
    push(x);
    for(int f;nroot(x);rotate(x)){
        f=fa[x];
        if(nroot(f))
            rotate((get(x)==get(f))?f:x);
    }
}
void access(int x){
//  cout<<"access "<<x<<'\n';
    for(int y=0;x;y=x,x=fa[x]){
        splay(x);ch[x][1]=y;update(x);
//      cout<<" do "<<x<<' '<<fa[x]<<' '<<y<<'\n';
    }
}
void mroot(int x){
    access(x);splay(x);pushrev(x);
}
void link(int u,int v){
    mroot(u);fa[u]=v;
}
void cut(int u,int v){
    mroot(u);access(v);splay(v);
    fa[u]=ch[v][0]=0;
}
int find(int u){
    access(u);splay(u);
    while(ch[u][0]) u=ch[u][0];
    return u;
}
int query(int u,int v){
    mroot(u);access(v);splay(v);
    return minn[v];
}
int lc[4000010],rc[4000010],seg[4000010],cnt;
int ntr[400010],root[400010];
int insert(int l,int r,int pre,int pos){
    int cur=++cnt,mid=(l+r)>>1;
    lc[cur]=lc[pre];rc[cur]=rc[pre];seg[cur]=seg[pre]+1;
    if(l==r) return cur;
    if(mid>=pos) lc[cur]=insert(l,mid,lc[pre],pos);
    else rc[cur]=insert(mid+1,r,rc[pre],pos);
    return cur;
}
int query(int l,int r,int ql,int qr,int pre,int cur){
    if(l>=ql&&r<=qr) return seg[cur]-seg[pre];
    int mid=(l+r)>>1,re=0;
    if(mid>=ql) re+=query(l,mid,ql,qr,lc[pre],lc[cur]);
    if(mid<qr) re+=query(mid+1,r,ql,qr,rc[pre],rc[cur]);
    return re;
}
int n,m,q;
void init(){
    n=read();m=read();q=read();cnt=0;
    memset(lc,0,sizeof(lc));memset(rc,0,sizeof(rc));memset(seg,0,sizeof(seg));
    for(int i=1;i<=n;i++) fa[i]=ch[i][0]=ch[i][1]=0,minn[i]=i,val[i]=1e9,rev[i]=0;
    val[0]=1e9;
}
pair<int,int>e[200010];
int main(){
    int T=read(),i,t1,t2,tmp,w;
    while(T--){
        init();
//      cout<<n<<' '<<m<<' '<<q<<'\n';
        for(i=1;i<=m;i++){
            t1=read();t2=read();
            e[i]=mp(t1,t2);
//          cout<<"input "<<t1<<' '<<t2<<'\n';
            if(t1==t2){ntr[i]=i;continue;}
            if(find(t1)==find(t2)){
                tmp=query(t1,t2);w=val[tmp];
                ntr[i]=w;
                cut(tmp,e[w].first);
                cut(tmp,e[w].second);
            }
            else ntr[i]=0;
//          cout<<"passed "<<ntr[i]<<'\n';
            fa[n+i]=ch[n+i][0]=ch[n+i][1]=0;
            val[n+i]=i;minn[n+i]=n+i;rev[n+i]=0;
            link(n+i,t1);link(n+i,t2);
        }
        for(i=1;i<=m;i++) root[i]=insert(0,m,root[i-1],ntr[i]);
        for(i=1;i<=q;i++){
            t1=read();t2=read();
            printf("%d\n",n-query(0,m,0,t1-1,root[t1-1],root[t2]));
        }
    }
}

原文地址:https://www.cnblogs.com/dedicatus545/p/9745034.html

时间: 2024-10-07 10:27:48

[bzoj3514][CodeChef GERALD07] Chef ans Graph Queries [LCT+主席树]的相关文章

[CodeChef - GERALD07 ] Chef and Graph Queries

Read problems statements in Mandarin Chineseand Russian. Problem Statement Chef has a undirected graph G. This graph consists of N vertices and M edges. Each vertex of the graph has an unique index from 1 to N, also each edge of the graph has an uniq

【CodeChef】Chef and Graph Queries

Portal --> CC Chef and Graph Queries Solution 快乐数据结构题(然而好像有十分优秀的莫队+可撤销并查集搞法qwq) 首先考虑一种方式来方便一点地..计算一个图的联通块数量:我们可以考虑容斥,维护每个连通块的生成树,然后\(n-\)生成树边数就是答案了 这样有一个好,加边的时候比较好处理,但是光这样并不能解决我们的问题 ? 顺着这个思路思考,先不考虑时间复杂度,对于一个询问,考虑将编号为\(l\sim r\)的边一条一条加入第\(1\sim l-1\)条

[BZOJ3514]CodeChef MARCH14 GERALD07加强版(LCT+主席树)

3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2177  Solved: 834[Submit][Status][Discuss] Description N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. Input 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密.接下来M行,代表图中的每条边.接下来K行,每行两个整数L

Code Chef - Chef and Graph Queries

传送门 题目大意 给定一个$n$个点$m$条边的无向图$(n,m\leq 200000)$. 有$q$每次询问$(q\leq 200000)$,每次给定一个区间$L,R$,求仅保留编号$\in[L,R]$的边,原图连通块的数量. 题解 不难发现连通块数量可以通过总点数$-$最大生成森林的边集大小得到. 按照编号对边从小到大排序,用$LCT$动态维护最大生成森林,每次操作加边时,若两个点不连通,就直接连边即可. 否则,就把路径上编号最小的边断掉,再强行连上新的边.则当前的生成森林一定是最大的并且恰

GERALD07加强版:lct,主席树,边化点

Description:N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. 传送门. lct这么神仙的东西一个题解都不写怎么行??? 神仙思路啊. 其实不是很难但是的确不容易想到. 我们考虑答案是什么. 首先刚开始有n个点分别是联通块,然后你连了一些边使联通块减少了. 怎么减少的呢?就是区间的边的生成树上边的数量.因为如果不是生成树上的边,那么一定与生成树上的边成环了而不会合并联通块. 怎么判断边是不是区间内生成树上的边呢?判断依据就是它有没有和前面的边成环. 那么我

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

【LCT+主席树】BZOJ3514 Codechef MARCH14 GERALD07加强版

3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2023  Solved: 778[Submit][Status][Discuss] Description N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. Input 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密. 接下来M行,代表图中的每条边. 接下来K行,每行两个整

BZOJ 3514: Codechef MARCH14 GERALD07加强版( LCT + 主席树 )

从左到右加边, 假如+的边e形成环, 那么记下这个环上最早加入的边_e, 当且仅当询问区间的左端点> _e加入的时间, e对答案有贡献(脑补一下). 然后一开始是N个连通块, 假如有x条边有贡献, 答案就是N-x. 用LCT维护加边, 可持久化线段树维护询问. O(NlogN) ------------------------------------------------------------------------------------ #include<cstdio> #inc

BZOJ 3514 LCT+主席树

思路: //By SiriusRen #include <bits/stdc++.h> using namespace std; const int N=400500; int n,m,k,type,ch[N][2],fa[N],minn[N],rev[N],q[N],top,pre[N]; int root[N],tree[N*50],lson[N*50],rson[N*50],cnt,xx,yy,ans; struct Road{int x,y,wei;}road[N]; bool isr