题意描述
定义图的乘法:A×B生成一个新图C,其中任意a∈A,b∈B,都对应C图中一个新点(a,b),C图中任意两点(a,b)、(a',b')之间存在边,当且仅当A、B中存在边(a,a')、(b,b')。
给出一个图G,求G×G中有多少个连通块。
思路
这道题目好啊,一开始以为是不可做题系列……
关键就是要充分理解题意,建立形象化的问题模型。
新图上的点(a,b)和点(c,d)之间有一条边,当且仅当原图上存在边(a,c)、(b,d)。
这实际上相当于一次移动:让a和b同时移动到相邻位置,到达一个新的状态(c,d)。
因此两个状态处于同一个联通块,实质上就是要求它们能够通过上述操作互相变换。
这样问题就形象多了。
容易发现状态(a,b)能够到达状态(c,d),
当且仅当存在路径(不一定是简单路径)A=a-->c,B=b-->d,满足A和B的长度奇偶性相同。
谈到奇偶性,常用的模型是二分图。
记a所在的连通块是Ga,b所在的连通块是Gb,
(1)可以发现,如果|Ga|=1,那么Ga×Gb得到了|Gb|个独立的点(|Gb|=1同理)。
证明:显然。
(2)可以发现,如果Ga和Gb中至少有一个存在奇环(即不是二分图),那么Ga×Gb得到的任意两个状态之间都连通。
证明:对于任意两个状态(a,b)、(c,d),可以先对(a,b)进行若干次操作,使得b绕着一个奇环走任意圈数,可以发现每走一圈,a-->c和b-->d的奇偶性就变一次,而它们奇偶性一样就可以互相转化了。
(3)可以发现,如果Ga和Gb都是二分图,那么Ga×Gb得到的状态形成了两大连通块。
证明:对所有点进行黑白染色,那么根据a和b的颜色划分,只会存在这四种状态:(a=黑,b=白)、(a=黑,b=黑),(a=白,b=白)、(a=白,b=黑)。显然在二分图中,(a=黑,b=白)和(a=白,b=黑)都可以互相转化;(a=黑,b=黑)和(a=白,b=白)也都可以互相转化;而这两大阵营之间奇偶性不同,永远不可能互相转化。得证。
显然Ga×Gb和Gb×Gc不存在任何联系,Ga×Gb和Gc×Gd更不会存在任何联系,因此可以对G中任意一对连通块独立地统计,累加到答案里。具体地,记单点有unit个,二分图有even个,非二分图且点数>1的有odd个,那么答案为2*unit*n-unit*unit+2*odd*(odd+even)-odd*odd+2*even*even(这个自己算算就知道了,没什么好说的)。
#include <cstdio>
#define ll long long
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=1e5+5,M=2e5+5;
int n,m,u,v,i,j;
int En,flg,V,vis[N],col[N],fst[N],nxt[M*2],to[M*2];
ll ans,unit,even,odd;
void add(int u,int v) {
En++; nxt[En]=fst[u]; fst[u]=En; to[En]=v;
}
void dfs(int x,int p)
{
int j,v;
V++; col[x]=p; vis[x]=1;
for (j=fst[x];j;j=nxt[j])
{
v=to[j];
if (vis[v]) {
if (col[v]==col[x]) flg=1;
continue;
}
dfs(v,p^1);
}
}
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,m)
{
scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
rep(i,1,n)
if (!vis[i])
{
V=0; flg=0;
dfs(i,0);
if (V==1) unit++;
else if (flg) odd++;
else even++;
}
ans=2*unit*n-unit*unit+2*odd*(odd+even)-odd*odd+2*even*even;
printf("%lld\n",ans);
return 0;
}