Codeforces292D
题意:
给出n个点和m条无向边,每条无向边依次编号为1~m,给出q次询问,问删去[l,r]这些边后连通分量个数。其中,,。
思路:
并查集是可以用来求无向图连通分量的个数的:
并查集是指比如A和B是一类,A和C是一类,那么A、B、C为一类;
无向图的连通是指比如A和B连通,A和C连通,那么B和肯定也连通,A、B、C就为一个连通分量;
类比一下就知道是怎样操作的了。
删去[l,r]这些边肯定是很难操作的,所以还是考虑添边。
删去[l,r]这些边就相当于添上1~l-1和r+1~m这些边,所以可以想到要用前缀后缀并查集,即保存下每添一条边当时的par情况。
所以对于删掉[l,r]这些边来说,相当于把前l-1条边添好的情况x和后m-r条边添好的情况y来unite一下。
如何unite呢?
因为并查集肯定是在一个数组里面进行操作的,所以先另外开一个数组。先赋初值为x情况,然后遍历每个点,如果发现x情况和y情况点的par不同,就说明这两个par要进行合并,但是这个数组的初值是x情况的,所以要在新数组里把两个par合并,即unite(2,x,find(0,x,i),find(1,y,i)); 。
这样子做的复杂度是O(max(m,q)*n),并不会爆炸,所以不用tarjan缩点直接做就可以了。
代码:
int n,m;
int eg[10010][2];
int par[3][10010][510];//[0][][]表示前缀
void init(int ps)
{
int id=0;
if(ps==1)id=m+1;
for(int i=1;i<=n;i++)
par[ps][id][i]=i;
}
int find(int ps,int id,int x)
{
if(par[ps][id][x]==x)return x;
return par[ps][id][x]=find(ps,id,par[ps][id][x]);
}
void unite(int ps,int id,int x,int y)
{
x=find(ps,id,x);y=find(ps,id,y);
if(x==y)return;
par[ps][id][x]=y;
}
int psunite(int x,int y)
{
for(int i=1;i<=n;i++)
par[2][x][i]=par[0][x][i];
for(int i=1;i<=n;i++)
unite(2,x,find(0,x,i),find(1,y,i));
int ans=0;
for(int i=1;i<=n;i++)
if(find(2,x,i)==i)
ans++;
return ans;
}
int main()
{
int q,l,r;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&eg[i][0],&eg[i][1]);
init(0);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
par[0][i][j]=par[0][i-1][j];
unite(0,i,eg[i][0],eg[i][1]);
}
init(1);
for(int i=m;i>=1;i--)
{
for(int j=1;j<=n;j++)
par[1][i][j]=par[1][i+1][j];
unite(1,i,eg[i][0],eg[i][1]);
}
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&l,&r);
printf("%d\n",psunite(l-1,r+1));
}
return 0;
}
ACM训练联盟周赛D
题意:
求最大的连通分量中的点数,其他同上。
思路:
在上题的基础上每次psunite之后计数每个点的par就可以求出最大的来连通分量中的点数了。
代码:
int n,m;
int eg[10010][2];
int par[3][10010][510];//[0][][]表示前缀
int cnt[10010];
void init(int ps)
{
int id=0;
if(ps==1)id=m+1;
for(int i=1;i<=n;i++)
par[ps][id][i]=i;
}
int find(int ps,int id,int x)
{
if(par[ps][id][x]==x)return x;
return par[ps][id][x]=find(ps,id,par[ps][id][x]);
}
void unite(int ps,int id,int x,int y)
{
x=find(ps,id,x);y=find(ps,id,y);
if(x==y)return;
par[ps][id][x]=y;
}
int psunite(int x,int y)
{
for(int i=1;i<=n;i++)
par[2][x][i]=par[0][x][i];
for(int i=1;i<=n;i++)
unite(2,x,find(0,x,i),find(1,y,i));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
cnt[find(2,x,i)]++;
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,cnt[i]);
return ans;
}
int main()
{
int q,l,r;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d",&eg[i][0],&eg[i][1]);
init(0);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
par[0][i][j]=par[0][i-1][j];
unite(0,i,eg[i][0],eg[i][1]);
}
init(1);
for(int i=m;i>=1;i--)
{
for(int j=1;j<=n;j++)
par[1][i][j]=par[1][i+1][j];
unite(1,i,eg[i][0],eg[i][1]);
}
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&l,&r);
printf("%d\n",psunite(l-1,r+1));
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!