BZOJ3514 Codechef MARCH14 GERALD07加强版

Link
这道题需要一个比较巧妙的转化。
首先我们知道连通块个数等于点数减树边数。
假如现在是第(i)时刻,我们加入一条边形成了一个环,环上最早加入的边是(ntr_i=j)时刻的。相当于第(i)条边把第(ntr_i)条边挤出去了。(如果加入第(i)条边不会生成环,那么(ntr_i=0)。如果第(i)条边是个自环,那么(ntr_i=i)。)
那么对于询问(l,r),如果有(ntr_i<lle ile r),那么这第(i)条边边会对这个询问产生一个边数的贡献。
那么我们二维数点即可。因为是强制在线所以需要主席树。
至于这个(ntr),我们可以使用LCT维护动态生成树来求。
至于这个数组名称是什么意思,我怎么知道?

#include<bits/stdc++.h>
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[11],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
    void Put(char x){*oS++=x;if(oS==oT)Flush();}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
    void write(int x){int top=0;while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('
');}
}
using IO::read;using IO::write;using IO::Flush;
int min(int a,int b){return a<b? a:b;}
const int N=200007;
int n,m,Q,o,ntr[N];
struct edge{int u,v;}e[N];
namespace LCT
{
    int ch[N<<1][2],fa[N<<1],rev[N<<1],mn[N<<1];
#define lc ch[x][0]
#define rc ch[x][1]
    int nroot(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
    void pushup(int x)
    {
	mn[x]=x;
	if(lc) mn[x]=min(mn[x],mn[lc]);
	if(rc) mn[x]=min(mn[x],mn[rc]);
    }
    void pushrev(int x){swap(lc,rc),rev[x]^=1;}
    void pushdown(int x){if(!rev[x])return;rev[x]=0;if(lc)pushrev(lc);if(rc)pushrev(rc);}
    void pushall(int x){if(nroot(x))pushall(fa[x]);pushdown(x);}
    void rotate(int x)
    {
	int y=fa[x],z=fa[y],k=ch[y][1]==x;
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	fa[x]=z,fa[ch[x][!k]]=y,ch[y][k]=ch[x][!k],fa[y]=x,ch[x][!k]=y,pushup(y),pushup(x);
    }
    void splay(int x)
    {
	pushall(x);
	for(int y;nroot(x);rotate(x)) if(nroot(y=fa[x])) rotate((ch[fa[y]][0]==y)^(ch[y][0]==x)? x:y);
    }
    void access(int x){for(int y=0;x;x=fa[y=x])splay(x),rc=y,pushup(x);}
    void makeroot(int x){access(x),splay(x),pushrev(x);}
    int findroot(int x){access(x),splay(x);while(lc)x=lc;return splay(x),x;}
    void split(int x,int y){makeroot(x),access(y),splay(y);}
    void link(int x,int y){makeroot(x),fa[x]=y;}
    void cut(int x,int y){split(x,y),ch[y][0]=fa[x]=0,pushup(y);}
    int ask(int x,int y){return split(x,y),mn[y];}
#undef lc
#undef rc
}using namespace LCT;
void Kruskal()
{
    for(int i=1,u,v,x;i<=m;++i)
    {
	if((u=e[i].u)==(v=e[i].v)) {ntr[i]=i;continue;}
	if(findroot(u)==findroot(v)) x=ask(u,v),ntr[i]=x,cut(e[x].u,x),cut(e[x].v,x);
	link(u,i),link(v,i);
    }
}
namespace JiangTree
{
#define mid ((l+r)>>1)
    int L[N<<5],R[N<<5],sum[N<<5],root[N],cnt=0;
    void insert(int &p,int l,int r,int x)
    {
	L[++cnt]=L[p],R[cnt]=R[p],sum[cnt]=sum[p]+1,p=cnt;
	if(l==r) return ;
	x<=mid? insert(L[p],l,mid,x):insert(R[p],mid+1,r,x);
    }
    int query(int u,int v,int l,int r,int ql,int qr)
    {
	if(ql<=l&&r<=qr) return sum[v]-sum[u];
	return (ql<=mid? query(L[u],L[v],l,mid,ql,qr):0)+(qr>mid? query(R[u],R[v],mid+1,r,ql,qr):0);
    }
#undef mid
}using namespace JiangTree;
void solve()
{
    int ans=0;
    for(int i=1;i<=m;++i) insert(root[i]=root[i-1],0,m,ntr[i]);
    for(int l,r;Q;--Q) l=read()^(ans*o),r=read()^(ans*o),write(ans=n-query(root[l-1],root[r],0,m,0,l-1));
}
int main()
{
    n=read(),m=read(),Q=read(),o=read();
    for(int i=1;i<=m;++i) e[i].u=read()+m,e[i].v=read()+m;
    Kruskal(),solve();
    return Flush(),0;
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11991643.html