agc011c Squared Graph
给出一个 \(n\) 个点, \(m\) 条边的无向图。从这个图生成一个新的图,\((a,b)\) 和 \((a',b')\) 之间存在一条边如果 \(a\) 和 \(b\) 之间在原图有一条边, \(a'\) 到 \(b'\) 之间在原图有一条边。求新图有多少个连通块。
\(2\leq n\leq 10^5, 0\leq m\leq 2\times10^5\)
图中不存在自环。
首先画成一个 \(n\times n\) 的图,这就是最终的图。
如果要求新图有多少个连通块,那么考虑是否和老图的连通块有关。
先考虑简单的三个点的图,假设这三个点都联通,有两种情况
存在边 \((1,2), (2,3)\),画一画,发现新图存在两个连通块
存在边 \((1,2), (2,3), (1,3)\), 画一画,发现新图只有一个连通块
此时有一个猜想,是不是树就存在一个连通块,但是如果有环就有两个连通块
接着继续画,如果是四个点呢
存在边 \((1,2),(2,3),(3,4)\),发现新图存在两个联通快,和我的猜想一样
存在边 \((1,2),(2,3),(3,4),(1,4)\),发现新图存在两个连通块,gg! 这个和之前的猜想不一样!
存在边 \((1,2),(2,3),(1,3),(3,4)\),发现新图存在一个连通块
存在边 \((1,2),(2,3),(3,4),(1,4),(1,3),(2,4)\),发现新图存在一个连通块
根据进一步的画图,发现猜想出现了一个问题,但是可以确定新图连通块只存在 \(1\) 个或者 \(2\) 个连通块。
那么怎么分辨是一个还是两个呢?其实根据奇环和偶环有一个idea,是不是跟二分染色有关,如果可以二分染色,就是2,如果不可以就是1。
如何证明呢?
考虑将原图二分染色,如果原图可以二分染色,发现新图的边存在两种情况 \((0,0)\) 连 \((1,1)\),\((0,1)\) 连 \((1,0)\),那么 \((0,0)\) 的边永远连不到 \((0,1)\) ,就会存在 \(2\) 个连通块,相反就只有 \(1\) 个。
剩下的就一个问题,如果是两个连通块之间连边呢,同样,如果两个连通块都可以二分染色,就是 \(2\) 个连通块,否则新连起来的连通块就是 \(1\) 个连通块。
只需要统计一下可以二分染色的连通块个数,不可以的个数,以及只有一个点的连通块个数,然后计算一下就好了。
真的是一个非常巧妙的题目,可惜我一开始画图一直都没画对 Q^Q,早知道写个代码求连通块个数就好了。
时间复杂度: \(O(n+m)\)
空间复杂度: \(O(n+m)\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
int n,m;
vector<int>g[N];
int col[N],cnt=0,odd=0,even=0,one=0;
void dfs(int x,int c,bool &bi){
cnt++;
col[x]=c;
for(auto to:g[x]){
if(col[to]==-1){
dfs(to,c^1,bi);
}else if(col[to]!=c^1)bi=false;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
for(int i=1;i<=n;i++)col[i]=-1;
for(int i=1;i<=n;i++){
if(col[i]==-1){
bool bi=true;
cnt=0;
dfs(i,0,bi);
if(cnt!=1){
if(bi)even++;
else odd++;
}else one++;
}
}
// cerr<<"one="<<one<<" odd="<<odd<<" even="<<even<<endl;
ll ans=0;
ans+=1ll*n*one*2-1ll*one*one;
// cerr<<1ll*n*one*2-1ll*one*one<<endl;
ans+=1ll*even*odd*2+1ll*odd*odd;
ans+=1ll*even*even*2;
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号