题意:点数n(n <= 30000), 边数(m <= 90000)的图,询问 q(1<=q<=30000)。
对于每个询问(l, r),去掉(l,r)区间以外的所有点和其相关联的边,问剩下来的图的联通块的个数。
思路:分块+并查集
这题很容易想到分块, 难点是并查集的合并。
对询问离线分块排序以后,我们对 左端点在相同块号内的询问 一起处理。这些询问的右断点是递增的,
左端点在某个块内,我们对每个询问分成2个区域分别进行并查集处理,区域1:左端点所在的块的区域(并查集lp)
(点的个数sqrt(n))。区域2:除区域1以外的其它询问点(并查集tf)。由于右断点是递增的,区域2很容易用一个并查集处理。区域1对于每个询问,都重新处理一遍并查集。最后关键就是两个并查集的合并,由于并查集不能进行删除操作,我们需要做的是 利用并查集tf里面的信息而不破坏它,并且复杂度要符合题意。我们可以在维护并查集lp的同时让这两个并查集合并,即在并查集f中把我当前需要的点搬到并查集lp里面,而没有必要把并查集tf全部搬过来,而且这样做复杂度也不符合要求,用一个vis数组就可以做到
在实际处理的时候 并查集lp能处理到的点是整个询问的区域内的点。
//#pragma comment(linker, "/STACK:102400000") #include<cstdlib> #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> #include<list> #include<queue> #include<vector> #define tree int o,int l,int r #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define lo o<<1 #define ro o<<1|1 #define pb push_back #define mp make_pair #define ULL unsigned long long #define LL long long #define inf 0x7fffffff #define eps 1e-7 #define N 30009 /* 知识: 1.两个并查集的合并:用vis数组标记元素是否已经合并了!(复杂度与find函数一致,很小) */ using namespace std; int m,n,T,t,bit,ns,tp; struct node { int l,r,id,b; bool operator<(const node a)const { return b<a.b||(b==a.b&&r<a.r); } } q[N]; vector<int>g[N]; int ans[N],tf[N],lf[N],vis[N]; void init() { for(int i=0; i<=n; i++) { g[i].clear(); } memset(vis,-1,sizeof(vis)); } void initset(int f[],int l,int r) { while(l<=r) { f[l]=l; l++; } } int find(int f[],int x) { return f[x]==x?x:f[x]=find(f,f[x]); } int unionone(int f[],int u,int v) { int tu=find(f,u); int tv=find(f,v); f[tu]=tv; return tu!=tv; } int findtwe(int x,int k) { if(vis[x]!=k) { lf[x]=tf[x]; vis[x]=k; } return lf[x]==x?x:lf[x]=findtwe(lf[x],k); } int uniontwe(int u,int v,int k) { int tu=findtwe(u,k); int tv=findtwe(v,k); lf[tu]=tv; return tu!=tv; } int fenkuai(int k,int &last) { tp+=q[k].r-last+1;//必须记住上一次得到的集合的个数,WA了多次 initset(tf,last,q[k].r); for(int i=last; i<=q[k].r; i++) { for(int j=0; j<g[i].size(); j++) { int v=g[i][j]; if(v>=(q[k].b*bit+1)&&v<=q[k].r) tp-=unionone(tf,i,v); } } int ans=tp+q[k].b*bit-q[k].l+1;/////////////WA initset(lf,q[k].l,q[k].b*bit);//q[k].b*bit不会大于n for(int i=q[k].l; i<=q[k].b*bit; i++)vis[i]=k; for(int i=q[k].l; i<=q[k].b*bit; i++) { for(int j=0; j<g[i].size(); j++) { int v=g[i][j]; if(v>=q[k].l&&v<=q[k].r) ans-=uniontwe(i,v,k); } } last=q[k].r+1; return ans; } int bfans(int l,int r) { int ans=r-l+1; initset(lf,l,r); for(int i=l+1; i<=r; i++) { for(int j=0; j<g[i].size(); j++) { int v=g[i][j]; if(v>=l&&v<=r) ans-=unionone(lf,i,v); } } return ans; } void solve() { for(int i=0; i<m;) { int b=q[i].b,last=(q[i].b)*bit+1; tp=0; while(i<m&&q[i].b==b) { if(q[i].r<(q[i].b)*bit+1) { ans[q[i].id]=bfans(q[i].l,q[i].r); } else { ans[q[i].id]=fenkuai(i,last); } i++; } } } int main() { #ifndef ONLINE_JUDGE freopen("ex.in","r",stdin); #endif int ncase=0; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for (int i=1; i<=m; ++i ) { int x,y; scanf("%d%d",&x,&y); g[x].push_back(y); g[y].push_back(x); } bit=sqrt(n+0.5); ns=(n+bit-1)/bit; scanf("%d",&m); for(int i=0; i<m; ++i) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i;q[i].b=(q[i].l+bit-1)/bit; } sort(q,q+m); solve(); printf("Case #%d: ",++ncase); for(int i=0; i<m; ++i) printf("%d ",ans[i]); } return 0; }