bzoj3514 Codechef MARCH14 GERALD07加强版
各种脑残错误调了半个下午+晚上半小时……先是把最大生成树写成了最小生成树,然后是初始化的值忘了改,最后还把主席树写挂了……这人没救了
我说那个PE只是我好奇为什么只有一个PE才故意去试的你信么……
言归正传……
这题是在ysq的博客上看见的,看完题解之后我的感想就是:妙,妙啊……(感觉一道思路好题就这样变成无脑练码力的题了……说的跟你能自己想出来似的
还是写个详细点的题解吧……
(约定提到的最小/大均为以编号为关键字)
对于每个询问的区间,我们可以只取出区间中所有边组成的最小生成森林,显然这样做对答案是没有影响的,因此我们的任务就变成了在线询问某个区间中所有边组成的最小生成森林的边数。注意这里的区间同时也是权值区间,这给我们带来了一些特殊性质。
考虑逆序添加所有边并动态维护最小生成树,但这样似乎没法得到关于某个区间中所有边组成的最小生成森林的什么信息,除非你搞一个可持久化LCT+块状链表出来,但显然这是不可能的……
因此考虑正序添加所有边并动态维护最大生成树,那么可以分类讨论一下:
边的两端不连通:显然所有包括它的区间的最小生成森林中都会有它(因为它是所有连接两个连通块的边中权值最小的那一个)。
边的两端连通:加入这条边后会形成一个环,这时一定会删除环上最早加入的那条边(因为维护的是最大生成树),记被删除的边编号为id,那么所有包含当前边且左端点>id的区间的最小生成森林中都会有当前边,而其他区间的最小生成森林中都不会有这条边(左端点≤id的区间中id号边还存在,由最小生成森林的定义可知应由id号边代替当前边;不包含当前边的区间的最小生成森林里怎么可能会有这条边……)。
如果定义边的两端不连通时被删除边的编号为0,那么问题就变成了每次询问区间[l,r]中因加入这条边而被删除的边的编号<l的边数(好像有点绕……)。显然这是主席树模板题,主席树裸上即可。
到这里我们的做法也就呼之欲出了:正序把所有边加进来,用LCT维护最大生成树并记录每条边删掉的边的编号(没删掉则为0),最后用主席树在线回答每个询问,复杂度O((n+m)logn)。
1 /************************************************************** 2 Problem: 3514 3 User: hzoier 4 Language: C++ 5 Result: Accepted 6 Time:33584 ms 7 Memory:93752 kb 8 ****************************************************************/ 9 #include<cstdio> 10 #include<cstring> 11 #include<algorithm> 12 #include<map> 13 #define isroot(x) ((x)->p==null||((x)->p->ch[0]!=(x)&&(x)->p->ch[1]!=(x))) 14 #define dir(x) ((x)==(x)->p->ch[1]) 15 using namespace std; 16 const int maxn=200010; 17 struct node{ 18 int key,mn,pos; 19 bool rev; 20 node *ch[2],*p; 21 node(int key=(~0u)>>1):key(key),mn(key),pos(-1),rev(false){} 22 inline void pushdown(){ 23 if(!rev)return; 24 ch[0]->rev^=true; 25 ch[1]->rev^=true; 26 swap(ch[0],ch[1]); 27 if(pos!=-1)pos^=1; 28 rev=false; 29 } 30 inline void refresh(){ 31 mn=key; 32 pos=-1; 33 if(ch[0]->mn<mn){ 34 mn=ch[0]->mn; 35 pos=0; 36 } 37 if(ch[1]->mn<mn){ 38 mn=ch[1]->mn; 39 pos=1; 40 } 41 } 42 }null[maxn<<1],*ptr=null; 43 node *newnode(int); 44 node *access(node*); 45 void makeroot(node*); 46 void link(node*,node*); 47 void cut(node*,node*); 48 node *getroot(node*); 49 node *getmin(node*,node*); 50 void splay(node*); 51 void rot(node*,int); 52 void build(int,int,int&,int); 53 void query(int,int,int,int); 54 int sm[maxn<<5]={0},lc[maxn<<5]={0},rc[maxn<<5]={0},root[maxn]={0},cnt=0; 55 map<node*,pair<node*,node*> >mp; 56 node *tmp; 57 int n,m,q,tp,x,y,k,l,r,t,ans=0; 58 int main(){ 59 null->ch[0]=null->ch[1]=null->p=null; 60 scanf("%d%d%d%d",&n,&m,&q,&tp); 61 for(int i=1;i<=n;i++)newnode((~0u)>>1); 62 for(int i=1;i<=m;i++){ 63 scanf("%d%d",&x,&y); 64 if(x==y){ 65 root[i]=root[i-1]; 66 continue; 67 } 68 if(getroot(null+x)!=getroot(null+y)){ 69 tmp=newnode(i); 70 k=0; 71 } 72 else{ 73 tmp=getmin(null+x,null+y); 74 cut(tmp,mp[tmp].first); 75 cut(tmp,mp[tmp].second); 76 k=tmp->key; 77 tmp->key=i; 78 tmp->refresh(); 79 } 80 link(tmp,null+x); 81 link(tmp,null+y); 82 mp[tmp]=make_pair(null+x,null+y); 83 build(0,m-1,root[i],root[i-1]); 84 } 85 while(q--){ 86 scanf("%d%d",&l,&r); 87 if(tp){ 88 l^=ans; 89 r^=ans; 90 } 91 ans=n; 92 t=--l; 93 query(0,m-1,root[r],root[l]); 94 printf("%d\n",ans); 95 } 96 return 0; 97 } 98 node *newnode(int x){ 99 *++ptr=node(x); 100 ptr->ch[0]=ptr->ch[1]=ptr->p=null; 101 return ptr; 102 } 103 node *access(node *x){ 104 node *y=null; 105 while(x!=null){ 106 splay(x); 107 x->ch[1]=y; 108 (y=x)->refresh(); 109 x=x->p; 110 } 111 return y; 112 } 113 void makeroot(node *x){ 114 access(x); 115 splay(x); 116 x->rev^=true; 117 } 118 void link(node *x,node *y){ 119 makeroot(x); 120 x->p=y; 121 } 122 void cut(node *x,node *y){ 123 makeroot(x); 124 access(y); 125 splay(y); 126 y->ch[0]->p=null; 127 y->ch[0]=null; 128 y->refresh(); 129 } 130 node *getroot(node *x){ 131 x=access(x); 132 while(x->pushdown(),x->ch[0]!=null)x=x->ch[0]; 133 splay(x); 134 return x; 135 } 136 node *getmin(node *x,node *y){ 137 makeroot(x); 138 x=access(y); 139 while(x->pushdown(),x->pos!=-1)x=x->ch[x->pos]; 140 splay(x); 141 return x; 142 } 143 void splay(node *x){ 144 x->pushdown(); 145 while(!isroot(x)){ 146 if(!isroot(x->p))x->p->p->pushdown(); 147 x->p->pushdown(); 148 x->pushdown(); 149 if(isroot(x->p)){ 150 rot(x->p,dir(x)^1); 151 break; 152 } 153 if(dir(x)==dir(x->p))rot(x->p->p,dir(x->p)^1); 154 else rot(x->p,dir(x)^1); 155 rot(x->p,dir(x)^1); 156 } 157 } 158 void rot(node *x,int d){ 159 node *y=x->ch[d^1]; 160 if((x->ch[d^1]=y->ch[d])!=null)y->ch[d]->p=x; 161 y->p=x->p; 162 if(!isroot(x))x->p->ch[dir(x)]=y; 163 (y->ch[d]=x)->p=y; 164 x->refresh(); 165 y->refresh(); 166 } 167 void build(int l,int r,int &rt,int pr){ 168 sm[rt=++cnt]=sm[pr]+1; 169 if(l==r)return; 170 lc[rt]=lc[pr]; 171 rc[rt]=rc[pr]; 172 int mid=(l+r)>>1; 173 if(k<=mid)build(l,mid,lc[rt],lc[pr]); 174 else build(mid+1,r,rc[rt],rc[pr]); 175 } 176 void query(int l,int r,int rt,int pr){ 177 if(!rt&&!pr)return; 178 if(t>=r){ 179 ans-=sm[rt]-sm[pr]; 180 return; 181 } 182 int mid=(l+r)>>1; 183 query(l,mid,lc[rt],lc[pr]); 184 if(t>mid)query(mid+1,r,rc[rt],rc[pr]); 185 } 186 /* 187 如果是离线的话,我们可以LCT+莫队什么的乱搞是吧,但是在线就…… 188 不过还是有一个很喵的做法—— 189 我们用LCT维护一棵生成树,当加入一条边i的时候(i是其编号),其连接的两个点可能已经联通,加入i之后会形成一个环,我们弹掉这个环上编号最小的边(也就是加入最早的边),并记录其编号为ntr_i。特殊的,如果i没有弹掉任何边,我们记ntr_i=0。 190 对于一个询问[L,R](表示我们只保留e|e∈[L,R]),答案就是$n?\sum_{i=L}^R(ntr_i<L)$。这个就是主席树了。 191 ——YouSiki 192 193 这做法简直是妙啊…… 194 */
再%一发ysq,妙,妙啊……