bzoj3514
对于这道题,想都没想,毫不犹豫的看了题解,这样就变成了只是练习写代码,没有思考。觉得自己这样不好。必须改。
参考题解:
http://wenku.baidu.com/view/a611cec4dd3383c4bb4cd2d8.html
http://blog.csdn.net/jiangyuze831/article/details/41694643
简单证明:
求(L,R)的联通块个数,是相当于往一个只有点1到n的,没有边的图中添加边(L,R),假设边X ,L<=X<=R,X添加后,可能会少一个联通块,也可能不回少,在于它所连接的那两个点是不是已经是联通的,但是这个联通是要求用(L,X-1)这些边造成的联通,所以要始终维护一棵树,(维护一棵树的意义就是,不连通的两个点因为加入某条边Y联通后,就一直处于联通装态,不会断开,同时维护最大生成树(每条边的编号是它的权值),使得树又是用离Y最近的边构成的),因此,在加入X时,X连接的两个点,有三种情况:
1.两点不连通,既然(1,X-1)都无法是这两个点联通,那么(L,X-1)也无法是这两个点联通。加入X后,会使得联通块个数减少1。
2.两点联通,且两点之间路径上的最小值小于L。如果存在(L,X-1)使得这两个点联通的话,那么此时最小值就一定大于等于L,(L,X-1)无法使得这两个点联通。加入X后,会联通块个数减少1。
3.两点联通,且两点之间路径上的最大值大于等于L,说明(L,X-1)可以使得这两个点联通,加入X后,不会使得联通块个数减少.
4.X链接的两点相同,那么此时联通块个数不会减少。
综上所述,用lct维护一棵最大生成树,数组num[i] 记载,加入某条边 i 时,两点不连通,则num[i]=0,如果两点联通,则记录路径上最小权值,特别的,如果一条边的两点相同时,num[i]=n+1(n是边的个数)。
代码:
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; #define N 200010 #define INF 0x3f3f3f3f struct node{ node *fa; node *ch[2]; int rev; int val; int minval; int valnum; int minvalnum;//用来记录以某个点为根节点的子树的最大值,而不是左子树的最大值,否则可能会错(不知道有没有严格的证明来证明绝对不会出错?所以这里只写可能会出错) int from;//涉及到维护生成树时,要有from和to int to; int init(int tempval,int tempvalnum,int tempfrom,int tempto){ ch[0]=ch[1]=fa=NULL; rev=0; val=tempval; minval=val; valnum=tempvalnum; minvalnum=valnum; from=tempfrom; to=tempto; } bool isroot(){ /*if(this==NULL){//这个输出了 printf("hahaha\n"); }*/ return fa==NULL||(fa->ch[0]!=this&&fa->ch[1]!=this); } void fswitch(){ rev^=1; swap(ch[0],ch[1]); } void push_down(){ if(rev){ if(ch[0]){ ch[0]->fswitch(); } if(ch[1]){ ch[1]->fswitch(); } rev=0; } return; } void go(){ /*if(this==NULL){//这里输出了 printf("wo shi da hao ren"); }*/ if(!isroot()){ fa->go(); } push_down(); return; } int dir(){ return fa->ch[1]==this?1:0; } void setedge(int d,node *another){ ch[d]=another; if(another){ another->fa=this;//这里是this,不是NULL } return; } void push_up(){ minvalnum=valnum; minval=val; if(ch[0]){ if(minvalnum>ch[0]->minvalnum){ minval=ch[0]->minval; minvalnum=ch[0]->minvalnum; } } if(ch[1]){ if(minvalnum>ch[1]->minvalnum){ minval=ch[1]->minval; minvalnum=ch[1]->minvalnum; } } return; } void rot(){ int d=dir(); node *tempfafa=fa->fa; if(!(fa->isroot())){ tempfafa->ch[fa->dir()]=this; } fa->setedge(d,ch[!d]); setedge(!d,fa); fa=tempfafa; ch[!d]->push_up(); return; } void splay(){ /*if(this==NULL){ printf("wo shi da hao ren");//这里输出了 }*/ go(); while(!isroot()){ if(!(fa->isroot())){ dir()==fa->dir()?fa->rot():rot(); } rot(); } push_up(); return; } void access(){ for(node *p=this,*q=NULL;p!=NULL;q=p,p=p->fa){ p->splay(); p->setedge(1,q); p->push_up(); } /*if(this==NULL){ printf("wo shi da hao ren");//这里输出了 }*/ splay(); return; } void make_root(){ /*if(this==NULL){//这里输出了 printf("wo sh da hao ren"); }*/ access(); fswitch(); } void cut(node *another){ /*if(another==NULL){//这里输出了 printf("wo shi da hao ren"); }*/ another->make_root(); access(); ch[0]->fa=NULL; ch[0]=NULL; push_up(); return; } void link(node *another){ another->make_root(); another->fa=this; return; } node *find_root(){ node *temp=this; while(temp->fa){//这里出现了死循环,说明某个temp->fa==temp,应该是前面lct的基本操作出了问题 /*if(temp==temp->fa){ printf("wo shi da hao ren"); }*/ temp=temp->fa; } return temp; } bool islink(node *another){ return find_root()==another->find_root()?true:false; } int query(node *another){ another->make_root(); access(); return minval; } }; node *tree[2*N],pool[2*N]; int num[N]; int Tree[N]; int c[30*N],lson[30*N],rson[30*N]; int cou;//记得初始化 int m; void addedge(int x,int y,int id){ if(x==y){ num[id]=m+1;//注意这里的return必须添加 return; } if(tree[x]->islink(tree[y])){ int tempval=tree[x]->query(tree[y]); if(tree[tempval]->valnum<id){ num[id]=tree[tempval]->valnum; tree[tempval]->cut(tree[tree[tempval]->from]); tree[tempval]->cut(tree[tree[tempval]->to]); tree[tempval]->init(tempval,id,x,y); tree[tempval]->link(tree[x]); tree[tempval]->link(tree[y]); } } else{ tree[++cou]=&(pool[cou]); tree[cou]->init(cou,id,x,y); tree[cou]->link(tree[x]); tree[cou]->link(tree[y]); num[id]=0; } return; } int build(int l,int r){ int root=cou++; c[root]=0; if(l!=r){ int middle=(l+r)>>1; lson[root]=build(l,middle); rson[root]=build(middle+1,r); } return root; } int insert(int froroot,int x){ int root=cou++,temproot=root; int l=0,r=m+1; c[temproot]=c[froroot]+1; while(l<r){ int middle=(l+r)>>1; if(x<=middle){ r=middle; lson[temproot]=cou++; rson[temproot]=rson[froroot]; temproot=lson[temproot]; froroot=lson[froroot]; } else{ l=middle+1; lson[temproot]=lson[froroot]; rson[temproot]=cou++; temproot=rson[temproot]; froroot=rson[froroot]; } c[temproot]=c[froroot]+1; } return root; } int query(int root,int l,int r,int x){ if(l==r){ if(l<x){ return c[root]; } else{ return 0; } } else{ int middle=(l+r)>>1; if(x<=middle){ return query(lson[root],l,middle,x); } else{ return c[lson[root]]+query(rson[root],middle+1,r,x); } } } int main(){ int n,k,type; int a,b; scanf("%d%d%d%d",&n,&m,&k,&type); for(int i=1;i<=n;i++){ tree[i]=&(pool[i]); tree[i]->init(i,INF,-1,-1); } cou=n;//这个要放对位置,否则会re for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); addedge(a,b,i); } Tree[0]=build(0,m+1); for(int i=1;i<=m;i++){ Tree[i]=insert(Tree[i-1],num[i]); } if(type){ int lastans=0; for(int i=0;i<k;i++){ scanf("%d%d",&a,&b); a=a^lastans; b=b^lastans; lastans=n-query(Tree[b],0,m+1,a)+query(Tree[a-1],0,m+1,a); printf("%d\n",lastans); } } else{ for(int i=0;i<k;i++){ scanf("%d%d",&a,&b); printf("%d\n",n-query(Tree[b],0,m+1,a)+query(Tree[a-1],0,m+1,a)); } } return 0; }