您的位置:首页 > 其它

BZOJ 3514 Codechef MARCH14 GERALD07加强版

2017-03-06 17:18 197 查看
题目链接:Codechef MARCH14 GERALD07加强版

  看到这种求连边求联通块个数的题,大概思路就是计算一下有多少条边连接了两个不同的联通块,然后用总点数减一下。

  然后我们该如何判断一条边是有效边呢?我们可以先用\(LCT\)来维护原图,当形成环的时候就把换上最早出现的边给弹掉。我们记\(a_i\)表示第\(i\)条边弹掉的边的编号(如果没有就为\(0\)),那么当我们只保留编号在\([l,r]\)内的边时,如果这其中的一条边\(x\)满足\(a_x<l\),那么这条边才是有用的。即这条边连接了两个联通块,会使得联通块个数\(-1\)。

  于是使用\(LCT\)预处理之后用主席树维护一下\(a\)数组就做完了。

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define isroot(x) (x!=s[fa[x]][0] && x!=s[fa[x]][1])
#define maxn 200010
#define MAXN maxn*20
#define INF (1<<29)

using namespace std;
typedef long long llg;

struct data{
int u,v;
}a[maxn];
int n,m,q,ty,ff[maxn],d[maxn<<1],val[maxn<<1],tt;
int fa[maxn<<1],s[maxn<<1][2],minv[maxn<<1];
int rt[maxn],sumv[MAXN],le[MAXN],ri[MAXN],L,ans;
bool rev[maxn<<1];

int getint(){
int w=0;bool q=0;
char c=getchar();
while((c>'9'||c<'0')&&c!='-') c=getchar();
if(c=='-') c=getchar(),q=1;
while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
return q?-w:w;
}

int find(int x){return ff[ff[x]]==ff[x]?ff[x]:ff[x]=find(ff[x]);}
void update(int u){
int l=s[u][0],r=s[u][1]; minv[u]=u;
if(val[minv[l]]<val[minv[u]]) minv[u]=minv[l];
if(val[minv[r]]<val[minv[u]]) minv[u]=minv[r];
}

void rotate(int u){
int p=fa[u],g=fa[p];
bool l=(u==s[p][1]),r=!l;
if(!isroot(p)) s[g][p==s[g][1]]=u;
fa[s[u][r]]=p; s[p][l]=s[u][r];
s[u][r]=p; fa[p]=u; fa[u]=g;
update(p); update(u);
}

void splay(int u){
int ld=0; d[ld=1]=u;
for(int i=u;!isroot(i);i=fa[i]) d[++ld]=fa[i];
for(int i=ld,x;x=d[i],i;i--)
if(rev[x]){
rev[s[x][0]]^=1,rev[s[x][1]]^=1;
swap(s[x][0],s[x][1]); rev[x]=0;
}
while(!isroot(u)){
int p=fa[u],g=fa[p];
if(!isroot(p)){
if((u==s[p][1])^(p==s[g][1])) rotate(u);
else rotate(p);
}
rotate(u);
}
}

void access(int u){for(int t=0;u;t=u,u=fa[u]) splay(u),s[u][1]=t,update(u);}
void makert(int u){access(u); splay(u); rev[u]^=1;}
void link(int x,int y){makert(x); fa[x]=y;}
void cut(int x,int y){
makert(x); access(y),splay(y);
s[y][0]=fa[x]=0; update(y);
}

int query1(int x,int y){
makert(x); access(y); splay(y);
return minv[y];
}

int add(int u,int l,int r){
int now=++tt,mid=(l+r)>>1;
sumv[now]=sumv[u]+1;
le[now]=le[u]; ri[now]=ri[u];
if(l!=r){
if(L<=mid) le[now]=add(le[u],l,mid);
else ri[now]=add(ri[u],mid+1,r);
}
return now;
}

int query2(int u,int v){
int l=0,r=m,mid,now=0;
while(l!=r){
mid=(l+r)>>1;
if(L<=mid) u=le[u],v=le[v],r=mid;
else now+=sumv[le[u]]-sumv[le[v]],u=ri[u],v=ri[v],l=mid+1;
}
return now;
}

int main(){
File("a");
n=getint(); m=getint(); q=getint(); ty=getint();
for(int i=1;i<=n;i++) ff[i]=i,val[i]=INF; val[0]=INF;
for(int i=1,u,v,x;i<=m;i++){
a[i].u=u=getint(); a[i].v=v=getint(); x=n,val[i+n]=i;
if(find(u)!=find(v)) ff[find(u)]=find(v);
else if(u!=v) x=query1(u,v),cut(a[x-n].u,x),cut(a[x-n].v,x);
if(u!=v) link(u,i+n),link(v,i+n); else x+=m;
L=x-n,rt[i]=add(rt[i-1],0,m);
}
while(q--){
int l=getint(),r=getint();
if(ty) l^=ans,r^=ans; L=l;
printf("%d\n",ans=n-query2(rt[r],rt[l-1]));
}
return 0;
}


  这种码农题写完就过的感觉真爽(爆数组\(RE\)的那一发不算)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: