BZOJ3514:GERALD07加强版(LCT,主席树)
Description
N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
Input
第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。
接下来M行,代表图中的每条边。
接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。
Output
K行每行一个整数代表该组询问的联通块个数。
Sample Input
3 5 4 0
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2
Sample Output
2
1
3
1
1
3
1
HINT
对于100%的数据,1≤N、M、K≤200,000。
Solution
$namespace$真是个好东西QAQ
每次往里加边,如果构成环的话就把最早的那条边删掉,这个可以用$LCT$随便做。
定义一个数组$Early[i]$,表示加第$i$条边的时候,会把哪条边删掉。
特殊的,如果不删边,$Early[i]=0$。如果第$i$条边是自环,$Early[i]=i$。
然后对$Early$数组建主席树,每次询问的答案就是$[l,r]$区间内小于等于$l-1$的数个个数。
正确性……如果一条边$i$的$Early[i]$在$l$后面的话,那么加入$i$的时候,因为会产生环所以不会对连通块情况产生影响。否则如果$Early[i]$在$l$的前面的话,加入$i$就会对连通块情况产生影响……
大体就是这样子具体我也不会
Code
1 #include<iostream> 2 #include<cstdio> 3 #define N (400009) 4 using namespace std; 5 6 int n,m,k,opt,Early[N],x[N],y[N]; 7 8 namespace LCT 9 { 10 int Father[N],Son[N][2],Val[N],Min[N],Rev[N]; 11 12 int Get(int x) 13 { 14 return Son[Father[x]][1]==x; 15 } 16 int Is_root(int x) 17 { 18 return Son[Father[x]][0]!=x && Son[Father[x]][1]!=x; 19 } 20 void Pushup(int x) 21 { 22 Min[x]=x; 23 int ls=Son[x][0],rs=Son[x][1]; 24 if (Val[Min[ls]]<Val[Min[x]]) Min[x]=Min[ls]; 25 if (Val[Min[rs]]<Val[Min[x]]) Min[x]=Min[rs]; 26 } 27 void Pushdown(int x) 28 { 29 if (Rev[x]) 30 { 31 Rev[Son[x][0]]^=1; 32 Rev[Son[x][1]]^=1; 33 swap(Son[x][0],Son[x][1]); 34 Rev[x]=0; 35 } 36 } 37 void Rotate(int x) 38 { 39 int wh=Get(x); 40 int fa=Father[x],fafa=Father[fa]; 41 if (!Is_root(fa)) Son[fafa][Son[fafa][1]==fa]=x; 42 Father[fa]=x; Son[fa][wh]=Son[x][wh^1]; 43 if (Son[fa][wh]) Father[Son[fa][wh]]=fa; 44 Father[x]=fafa; Son[x][wh^1]=fa; 45 Pushup(fa); Pushup(x); 46 } 47 void Push(int x) 48 { 49 if (!Is_root(x)) Push(Father[x]); 50 Pushdown(x); 51 } 52 void Splay(int x) 53 { 54 Push(x); 55 for (int fa; !Is_root(x); Rotate(x)) 56 if (!Is_root(fa=Father[x])) 57 Rotate(Get(fa)==Get(x)?fa:x); 58 } 59 void Access(int x) 60 { 61 for (int y=0; x; y=x,x=Father[x]) 62 Splay(x), Son[x][1]=y, Pushup(x); 63 } 64 void Make_root(int x) 65 { 66 Access(x); Splay(x); Rev[x]^=1; 67 } 68 int Find_root(int x) 69 { 70 Access(x); Splay(x); 71 while (Son[x][0]) x=Son[x][0]; 72 return x; 73 } 74 void Link(int x,int y) 75 { 76 Make_root(x); Father[x]=y; 77 } 78 void Cut(int x,int y) 79 { 80 Make_root(x); Access(y); Splay(y); 81 Son[y][0]=Father[x]=0; Pushup(y); 82 } 83 int Query(int x,int y) 84 { 85 Make_root(x); Access(y); Splay(y); 86 return Min[y]; 87 } 88 void Build_Early() 89 { 90 for (int i=1; i<=m; ++i) Val[n+i]=i; 91 for (int i=0; i<=n; ++i) Val[i]=2e8,Min[i]=i; 92 for (int i=1; i<=m; ++i) 93 { 94 scanf("%d%d",&x[i],&y[i]); 95 if (x[i]==y[i]) {Early[i]=i; continue;} 96 if (Find_root(x[i])!=Find_root(y[i])) 97 Link(x[i],i+n), Link(y[i],i+n); 98 else 99 { 100 int p=Query(x[i],y[i]); 101 Early[i]=p-n; 102 Cut(x[p-n],p); Cut(y[p-n],p); 103 Link(x[i],i+n); Link(y[i],i+n); 104 } 105 } 106 } 107 } 108 109 namespace Sgt 110 { 111 struct Sgt{int ls,rs,val;}Segt[N*40]; 112 int sgt_num,Root[N]; 113 114 int Update(int pre,int l,int r,int x) 115 { 116 int now=++sgt_num; 117 Segt[now]=Segt[pre]; 118 Segt[now].val++; 119 if (l==r) return now; 120 int mid=(l+r)>>1; 121 if (x<=mid) Segt[now].ls=Update(Segt[now].ls,l,mid,x); 122 else Segt[now].rs=Update(Segt[now].rs,mid+1,r,x); 123 return now; 124 } 125 int Query(int u,int v,int l,int r,int l1,int r1) 126 { 127 if (l>r1 || r<l1) return 0; 128 if (l1<=l && r<=r1) return Segt[v].val-Segt[u].val; 129 int mid=(l+r)>>1; 130 return Query(Segt[u].ls,Segt[v].ls,l,mid,l1,r1)+Query(Segt[u].rs,Segt[v].rs,mid+1,r,l1,r1); 131 } 132 void Solve() 133 { 134 for (int i=1; i<=m; ++i) 135 Root[i]=Update(Root[i-1],0,2e5,Early[i]); 136 int ans=0,l,r; 137 for (int i=1; i<=k; ++i) 138 { 139 scanf("%d%d",&l,&r); 140 if (opt) l^=ans, r^=ans; 141 ans=n-Query(Root[l-1],Root[r],0,2e5,0,l-1); 142 printf("%d\n",ans); 143 } 144 } 145 } 146 147 int main() 148 { 149 scanf("%d%d%d%d",&n,&m,&k,&opt); 150 LCT::Build_Early(); 151 Sgt::Solve(); 152 }