传送门:Codeforces292D Connected Components
题目大意
一个无向图,给出m条边,有k次询问,每次询问将第l到r条边暂时删去,求这时候有多少个连通分量。
解题思路
最朴素的思想,每次查询[a,b]的时候的时候,把从1到a-1和从b+1到最后做并查集,然后看有几个连通分量。但是很不幸的是这种做法的复杂度太高了,是o(m*n).
我们可以换一个想法用PL[i]这个数组存储前i个的并查集,用PR[i]存储从后往前的的后i个的并查集。每次查询[a,b]的时候,只要把PL[a-1]和PR[b+1]座并查集就可以了
#include<cstdio>
const int MAXN = 510;
const int MAXM = 10010;
int u[MAXM],v[MAXM];
struct dsu{
int pre[MAXN];
void initi()
{
for(int i=1;i<=MAXN;i++)
pre[i] = i;
}
int find(int x)
{
return (x==pre[x])?pre[x] : (pre[x] = find(pre[x]));
}
void join(int x,int y)
{
int fx = find(x),fy = find(y);
if(fx!=fy)
pre[fx] = fy;
}
}PL[MAXM],PR[MAXM];
int solve(dsu left,dsu right,int n)
{
for(int i=1;i<=n;i++)
right.join(i,left.find(i));
int ret = 0;
for(int i=1;i<=n;i++)
if(right.find(i) == i)
ret++;
return ret;
}
int main()
{
int n,m,a,b;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&u[i],&v[i]);
PL[0].initi();
PR[m+1].initi();
for(int i=1;i<=m;i++)
{
PL[i] = PL[i-1];
PL[i].join(u[i],v[i]);
}
for(int i=m;i>0;i--)
{
PR[i] = PR[i+1];
PR[i].join(u[i],v[i]);
}
int q;
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&a,&b);
int ans = solve(PL[a-1],PR[b+1],n);
printf("%d\n",ans);
}
return 0;
}