P11674 [USACO25JAN] Reachable Pairs G
显然正面直接并查集模拟,由于普及组算法无法处理分裂,所以排除,正难则反,倒着做就是合并所以考虑倒过来。
若只有操作
现在想操作
于是,
但倒着遍历也不能直接遍历到虚点就遍历他的所有点,所以我们提前处理他的影响,他连的实点不用管,在实点加入进来时合并就行,他连的虚点就提前加入一个并查集,这样复杂度正确且把点之间的关系都处理好了。
需要注意,虚点合并不能计算贡献,我们可以提前把虚点的
非虚点加入方式同只有操作
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200010
#define MAXM 800010
#define ll long long
int Next[MAXM],edge[MAXM],size[MAXN];
int head[MAXN],vis[MAXN],mk[MAXN];
int n,m,tot,fa[MAXN];
ll ans[MAXN],now;
char s[MAXN];
vector<int>p[MAXN],q[MAXN];
void add(int x,int y)
{
edge[++tot]=y;
Next[tot]=head[x];
head[x]=tot;
}
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void join(int x,int y)
{
int f1=find(x),f2=find(y);
if(f1!=f2){
now+=1ll*size[f1]*size[f2];
size[f2]+=size[f1],fa[f1]=f2;
}
}
void solve(int x)
{
for(int i=0;i<q[x].size();i++)
{
int y=q[x][i];
join(x,y);
if(!vis[y]&&mk[i]){
vis[y]=1;
solve(y);
}
}
}
int main(){
scanf("%d%d",&n,&m);
cin>>(s+1);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
fa[i]=i,size[i]=1;
for(int i=1;i<=n;i++)
{
mk[i]=s[i]-'0';
if(mk[i])size[i]--;
for(int j=head[i];j;j=Next[j])
{
int v=edge[j];
if(v>i||mk[v])p[i].push_back(v);
if(mk[v]&&mk[i])
join(v,i);
}
}
for(int i=n;i>=1;i--)
{
d[i]=1;
if(mk[i]){
now+=size[find(i)]++;
ans[i]=now;
if(vis[i])continue;
vis[i]=1;
}
for(int j=0;j<p[i].size();j++)
join(p[i][j],i);
ans[i]=now;
}
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]);
return 0;
}