zoukankan      html  css  js  c++  java
  • BZOJ 3514 Codechef MARCH14 GERALD07加强版 Link-Cut-Tree+划分树

    题目大意:

    给定n个点m条边的无向图。求问当图中仅仅有【编号在[l,r]区间内】的边存在时图中的联通块个数 强制在线

    注意联通块是指联通了就是同一块,不是Tarjan求的那种块

    看到这题的那一刻我就想小便有木有0.0 这尼玛怎么做?可持久化并查集? 暴力? 分块乱搞? 。。。

    后来看了HZWER大神的博客才知道这样的巧妙的算法0.0 太强大了

    直接复制wulala的题解 讲得非常清楚 不累述了


    wulala

    葱娘说这是一个非常巧妙的题。。
    有一个比較猎奇的做法:首先把边依次加到图中,若当前这条边与图中的边形成了环,那么把这个环中最早加进来的边弹出去
    并将每条边把哪条边弹了出去记录下来:ntr[i] = j,特别地,要是没有弹出边,ntr[i] = 0;
    这个显然是能够用LCT来弄的对吧。


    然后对于每一个询问,我们的答案就是对l~r中ntr小于l的边求和,并用n减去这个值
    正确性能够YY一下:
    假设一条边的ntr >= l,那么显然他能够与从l ~ r中的边形成环,那么它对答案没有贡献
    反之假设一条边的ntr < l那么它与从l ~ r中的边是不能形成环的。那么他对答案的贡献为-1
    对于查询从l ~ r中有多少边的ntr小于l,我反正是用的函数式线段树


    这个真是太强大了0.0 假设这条边踢掉的最早的边也在[l,r]区间内 那么增加这条边一定对图的连通性没有影响 否则就会连接两个联通块 导致ans--

    至于求l~r中有多少边的ntr小于l我用的是划分树 蒟蒻不会写主席树肿莫破。。

    ntr。寝取り,果然是个够劲的名字。

    。 于是为了保持这样的美好的意境我也牺牲了划分树中的a数组改成了ntr。。

    把这个美好的意境传承下去吧。。

    另外这题有自环 自环的话相当于自己ntr自己 直接记录ntr之后就返回好了


    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define M 200200
    #define INF 2147483647
    using namespace std;
    struct edges{
    	int x,y;
    }e[M];
    struct abcd{
    	abcd *fa,*ls,*rs;
    	int num,minnum;
    	bool rev_mark;
    	abcd(int x);
    	void Reverse();
    	void Push_Up();
    	void Push_Down();
    }*null=new abcd(INF),*tree[M<<1];
    abcd :: abcd(int x)
    {
    	fa=ls=rs=null;
    	num=minnum=x;
    	rev_mark=0;
    }
    void abcd :: Reverse()
    {
    	rev_mark^=1;
    	swap(ls,rs);
    }
    void abcd :: Push_Up()
    {
    	minnum=min(ls->minnum,rs->minnum);
    	minnum=min(minnum,num);
    }
    void abcd :: Push_Down()
    {
    	if(fa->ls==this||fa->rs==this)
    		fa->Push_Down();
    	if(rev_mark)
    	{
    		ls->Reverse();
    		rs->Reverse();
    		rev_mark=0;
    	}
    }
    void Zig(abcd *x)
    {
    	abcd *y=x->fa;
    	y->ls=x->rs;
    	x->rs->fa=y;
    	x->rs=y;
    	x->fa=y->fa;
    	if(y==y->fa->ls)
    		y->fa->ls=x;
    	else if(y==y->fa->rs)
    		y->fa->rs=x;
    	y->fa=x;
    	y->Push_Up();
    }
    void Zag(abcd *x)
    {
    	abcd *y=x->fa;
    	y->rs=x->ls;
    	x->ls->fa=y;
    	x->ls=y;
    	x->fa=y->fa;
    	if(y==y->fa->ls)
    		y->fa->ls=x;
    	else if(y==y->fa->rs)
    		y->fa->rs=x;
    	y->fa=x;
    	y->Push_Up();
    }
    void Splay(abcd *x)
    {
    	x->Push_Down();
    	while(x->fa->ls==x||x->fa->rs==x)
    	{
    		abcd *y=x->fa,*z=y->fa;
    		if(x==y->ls)
    		{
    			if(y==z->ls)
    				Zig(y);
    			Zig(x);
    		}
    		else
    		{
    			if(y==z->rs)
    				Zag(y);
    			Zag(x);
    		}
    	}
    	x->Push_Up();
    }
    void Access(abcd *x)
    {
    	abcd *y=null;
    	while(x!=null)
    	{
    		Splay(x);
    		x->rs=y;
    		x->Push_Up();
    		y=x;
    		x=x->fa;
    	}
    }
    abcd* Find_Root(abcd *x)
    {
    	while(x->fa!=null)
    		x=x->fa;
    	return x;
    }
    void Move_To_Root(abcd *x)
    {
    	Access(x);
    	Splay(x);
    	x->Reverse();
    }
    void Link(abcd *x,abcd *y)
    {
    	Move_To_Root(x);
    	x->fa=y;
    }
    void Cut(abcd *x,abcd *y)
    {
    	Move_To_Root(x);
    	Access(y);
    	Splay(y);
    	x->fa=null;
    	y->ls=null;
    	y->Push_Up();
    }
    int Query(abcd *x,abcd *y)
    {
    	Move_To_Root(x);
    	Access(y);
    	Splay(y);
    	return y->minnum;
    }
    int n,m,q,type,ans;
    int ntr[M],b[M],c[M],s[20][M];
    void Insert(int p)
    {
    	if(e[p].x==e[p].y)
    	{
    		ntr[p]=p;
    		return ;
    	}
    	abcd *x=tree[e[p].x],*y=tree[e[p].y];
    	if( Find_Root(x)==Find_Root(y) )
    	{
    		int temp=Query(x,y);
    		ntr[p]=temp;
    		Cut(tree[n+temp],tree[e[temp].x]);
    		Cut(tree[n+temp],tree[e[temp].y]);
    		free(tree[n+temp]);
    	}
    	tree[n+p]=new abcd(p);
    	Link(tree[n+p],tree[e[p].x]);
    	Link(tree[n+p],tree[e[p].y]);
    }
    void Build_Tree(int l,int r,int dpt)
    {
    	int i,mid=l+r>>1;
    	int l1=l,l2=mid+1;
    	int left=mid-l+1;
    	if(l==r)
    		return ;
    	for(i=l;i<=r;i++)
    		left-=(ntr[i]<c[mid]);
    	for(i=l;i<=r;i++)
    	{
    		if(ntr[i]<c[mid]||ntr[i]==c[mid]&&left)
    			b[l1++]=ntr[i],s[dpt][i]=(i==l?1:s[dpt][i-1]+1),left-=(ntr[i]==c[mid]);
    		else
    			b[l2++]=ntr[i],s[dpt][i]=(i==l?0:s[dpt][i-1]);
    	}
    	memcpy( ntr+l , b+l , sizeof(ntr[0])*(r-l+1) );
    	Build_Tree(l,mid,dpt+1);
    	Build_Tree(mid+1,r,dpt+1);
    }
    int Get_Ans(int l,int r,int dpt,int x,int y,int val)
    {
    	int mid=l+r>>1;
    	int l1=(x==l?0:s[dpt][x-1]),l2=s[dpt][y];
    	if(x>y)
    		return 0;
    	if(l==r)
    		return ntr[mid]<val;
    	if(val<=c[mid])
    		return Get_Ans(l,mid,dpt+1,l+l1,l+l2-1,val);
    	else
    		return l2-l1+Get_Ans(mid+1,r,dpt+1,(mid+1)+(x-l-l1),(mid+1)+(y-l+1-l2)-1,val);
    }
    int main()
    {
    	int i,x,y;
    	cin>>n>>m>>q>>type;
    	for(i=1;i<=n;i++)
    		tree[i]=new abcd(INF);
    	for(i=1;i<=m;i++)
    		scanf("%d%d",&e[i].x,&e[i].y),Insert(i);
    	memcpy(c+1,ntr+1,sizeof(c[0])*m);
    	sort(c+1,c+m+1);
    	Build_Tree(1,m,0);
    	for(i=1;i<=q;i++)
    	{
    		scanf("%d%d",&x,&y);
    		x^=ans*type;y^=ans*type;
    		ans=n-Get_Ans(1,m,0,x,y,x);
    		printf("%d
    ", ans );
    	}
    }
    


  • 相关阅读:
    查询死锁和处理死锁(SqlServer)
    日期函数(SqlServer)
    [Shell] echo/输出 中引用命令
    Github 团队协作基本流程与命令操作 图解git工作流程
    HTML 引入 CSS、JS 的三种方式
    JavaScript 字符串匹配 | JS 的正则用法 | 从后边匹配
    Sublime + Chrome 本地调试 CSS 选择器
    常用 CSS 选择器
    使用 Sublime 或其他编辑器调试 Tampermonkey 油猴脚本
    使用 chrome 扩展 Vimium 实现快捷键关闭其他标签页
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/6918930.html
Copyright © 2011-2022 走看看