题意
有一个n个点m条边的无向图G。现在构造一个有n*n个点的新图G’,每个点是一个数对(x,y)。新图中存在一条从(a,b)连向(c,d)的边当且仅当在原图中存在从a连向c的边和从b连向d的边。问新图中的连通块数量。
n<=100000,m<=200000
分析
首先我们发现若(a,b)存在路径到达(c,d),则必然在原图中存在一条从a到c的路径和一条从b到d的路径且这两条路径的长度相同(不一定是简单路径)。
那么若路径存在,则a和c必然是在一个连通块内,b和d一定是在一个连通块内。
现在考虑两个连通块之间的贡献,也就是那些满足第一维在第一个连通块,第二维在第二个连通块的点的贡献。。
手玩一下不难发现如果两个连通块都是二分图,我们可以搞出两个连通块。
若其中一个不是二分图,则只有一个连通块。
对于单独的点再特殊考虑一下。
剩下的就很简单了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=100005;
int n,m,cnt,last[N],dep[N],s;
bool vis[N],flag;
struct edge{int to,next;}e[N*4];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void dfs(int x,int fa)
{
vis[x]=1;s++;
for (int i=last[x];i;i=e[i].next)
if (vis[e[i].to]&&e[i].to!=fa) flag|=dep[x]==dep[e[i].to];
else if(!vis[e[i].to]) dep[e[i].to]=dep[x]^1,dfs(e[i].to,x);
}
int main()
{
n=read();m=read();
for (int i=1;i<=m;i++)
{
int x=read(),y=read();
addedge(x,y);
}
LL ans=0;int t1=0,t2=0,t3=0;
for (int i=1;i<=n;i++)
{
if (vis[i]) continue;
flag=0;s=0;
dfs(i,0);
if (s==1) ans+=n*2-1,t3++;
else if (!flag) ans+=2,t1++;
else ans++,t2++;
}
ans-=(LL)t3*(t3-1);
ans+=(LL)t1*(t1-1)*2+(LL)t2*(t2-1)+(LL)t1*t2*2;
printf("%lld",ans);
return 0;
}