zoukankan      html  css  js  c++  java
  • Code Chef

    传送门

    题目大意

    给定一个$n$个点$m$条边的无向图$(n,mleq 200000)$。

    有$q$每次询问$(qleq 200000)$,每次给定一个区间$L,R$,求仅保留编号$in[L,R]$的边,原图连通块的数量。

    题解

    不难发现连通块数量可以通过总点数$-$最大生成森林的边集大小得到。

    按照编号对边从小到大排序,用$LCT$动态维护最大生成森林,每次操作加边时,若两个点不连通,就直接连边即可。

    否则,就把路径上编号最小的边断掉,再强行连上新的边。则当前的生成森林一定是最大的并且恰好覆盖了每一个连通块。

    对于每一次询问,就是用$n$减去在最大的边编号为$R$时,最大生成森林中编号$in[L,R]$的数量。

    用主席树维护一下即可。复杂度$O((m+q)log n)$。

    由于在$LCT$中维护边权比较复杂,所以我们可以把一条边变成一个点,这个点连向原边的两段端点,点权即为边权,会方便许多。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define M 400020
    #define INF 3000020
    #define ls c[x][0]
    #define rs c[x][1]
    #define mid ((l+r)>>1)
    using namespace std;
    int read(){
    	int nm=0,fh=1; char cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
    int n,m,fa[M],c[M][2],u[M],v[M],e[M],rev[M];
    int L[M*30],R[M*30],sum[M*30],cnt,rt[M],S[M],top,tot;
    void pushup(int x){if(x) e[x]=(x>n?x-n:INF),e[x]=min(e[x],min(e[ls],e[rs]));}
    void pushdown(int x){if(rev[x]&&x) rev[x]=0,rev[ls]^=1,rev[rs]^=1,swap(ls,rs);}
    bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
    void rotate(int x){
    	int tp=fa[x],dtp=fa[fa[x]],ms,ds;
    	if(c[dtp][0]==tp) c[dtp][0]=x;
    	else if(c[dtp][1]==tp) c[dtp][1]=x;
    	if(c[tp][0]==x) ms=0,ds=1;else ms=1,ds=0;
    	fa[x]=dtp,fa[tp]=x,fa[c[x][ds]]=tp;
    	c[tp][ms]=c[x][ds],c[x][ds]=tp;
    	pushup(tp),pushup(x);
    }
    void splay(int x){
    	S[top=1]=x;
    	for(int y=x;!isroot(y);y=fa[y]) S[++top]=fa[y];
    	while(top) pushdown(S[top]),top--;
    	while(!isroot(x)){
    		int tp=fa[x];
    		if(isroot(tp)) return rotate(x);
    		else if(c[c[fa[tp]][0]][0]==x) rotate(tp);
    		else if(c[c[fa[tp]][1]][1]==x) rotate(tp);
    		else rotate(x);
    	}
    }
    int fdrt(int x){return fa[x]?fdrt(fa[x]):x;}
    void access(int x){for(int y=0;x;y=x,x=fa[x]) splay(x),rs=y,pushup(x);}
    void chroot(int x){access(x),splay(x),rev[x]^=1;}
    void link(int x,int y){chroot(x),splay(x),fa[x]=y;}
    void cut(int x,int y){chroot(x),access(y),splay(x),fa[y]=ls=rs=c[y][0]=c[y][1]=0,pushup(x),pushup(y);}
    int qry(int x,int y){chroot(x),access(y),splay(x);return (fdrt(y)!=x)?0:e[x];}
    void ins(int &x,int pre,int l,int r,int pos,int dt){
    	x=++cnt,L[x]=L[pre],R[x]=R[pre];
    	sum[x]=sum[pre]+dt; if(l==r) return;
    	if(pos<=mid)ins(L[x],L[pre],l,mid,pos,dt);
    	else ins(R[x],R[pre],mid+1,r,pos,dt);
    }
    int getans(int x,int l,int r,int minn){
    	if(r<minn||!sum[x]) return 0; if(l>=minn) return sum[x];
    	return getans(L[x],l,mid,minn)+getans(R[x],mid+1,r,minn);
    }
    int main(){
    	for(int T=read(),Q;T;T--,cnt=0){
    		n=read(),m=read(),Q=read(),tot=n,memset(c,0,sizeof(c));
    		memset(rt,0,sizeof(rt)),memset(fa,0,sizeof(fa)),memset(e,0x3f,sizeof(e));
    		for(int i=1;i<=m;i++){
    			int now; u[i]=read(),v[i]=read(),rt[i]=rt[i-1],tot++;
    			if(u[i]==v[i]) continue; now=qry(u[i],v[i]);
    			if(now)	ins(rt[i],rt[i],1,m,now,-1),cut(u[now],v[now]);
    			ins(rt[i],rt[i],1,m,i,1),link(u[i],tot),link(v[i],tot);
    		}
    		while(Q--){
    			int tl=read(),tr=read(),num;
    			num=getans(rt[tr],1,m,tl);
    			write(n-num),putchar('
    ');
    		}
    	}
    	return 0;
    }
  • 相关阅读:
    自学android半年,已从.net转型成android程序员,分享下这个过程(转)
    深入浅出jsonp
    对程序员说点实在话(转)
    浅谈iOS学习之路(转)
    Quartz Cron表达式 在线生成器
    AutoMapper使用笔记
    毕业两年工作三年小结
    C#转C++的一点分享
    手机数据抓包以及wireshark技巧
    这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧。。。(转)
  • 原文地址:https://www.cnblogs.com/OYJason/p/9744840.html
Copyright © 2011-2022 走看看