【图论】【思维】AGC011C Squared Graph

分析:

比较简单的思维题
首先,由于在新图中,每个点是原图的一个点对,观察这个新图中,两点间存在边的条件:
发现可以这么表示:在原图中又两个点 ( i , j ) (i,j) (i,j),分别向与其相邻的点走一步,到达 ( u , v ) (u,v) (u,v),那么 ( i , j ) (i,j) (i,j) ( u , v ) (u,v) (u,v)之间就有边相连。

这样表示在之后的推导中有重要意义。

假设我们考虑一个单一的联通块内部的情况:
首先,对于任意一个点对 ( u , v ) (u,v) (u,v),我们可以将两点均往中间缩,即 u u u v v v走一步, v v v u u u走一步。
持续这样操作,最后只有2种情况:
1、 u , v u,v u,v重合。(原本的路径长度为偶数)
2、 u , v u,v u,v在一条边的两侧。(原本的路径长度为奇数)。

我们分别定义这两种情况为:点联通块,边联通块。
很显然,所有单点对 ( i , i ) (i,i) (i,i)都在点联通块中,所有边都在边联通块中( ( i , j ) 满 足 有 边 ( i &lt; − &gt; j ) (i,j)满足有边(i&lt;-&gt;j) (i,j)(i<>j))

根据上面的推导,我们发现,整个原图的联通块,在新图中,就形成了2个联通块。但有种情况,会使得只有1个联通块。

观察我们上面的条件:路径长度分别为奇数,偶数。但如果联通块中有奇环,那么路径长度既可以有奇数,又可以有偶数。换言之,这一点对可以缩到点联通块中,又可以缩到边联通块中。所以,在这种情况下,这个联通块在新图中,就只有1个联通块。

现在考虑点对在两个联通块之间的情况:

有了上面的推导,不难发现这里其实也是一样的。对任意一个点对 ( i , a ) (i,a) (i,a)
如果两联通块中均无奇环,那么这个点对就不可能连到 ( i , b ) (i,b) (i,b)(b与a有边相连)。以及 ( j , a ) (j,a) (j,a)(i与j有边相连)等等。因为如果i走了偶数步,又回到了i,同时a也必须走偶数步,如果没有奇环,那么a永远不可能走到与其相邻的某个点上。

所以,把结论综合一下,发现:
对任意两个联通块,如果其中一个有奇环,那么这两个联通块在新图中就连在一起。否则就形成互相交叉的两个联通块。

注意,对于单点的联通块需要特殊处理。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
struct node{
	int x;
	node *nxt;	
}edge[MAXN*4];
node *head[MAXN],*ncnt=edge;
void add_edge(int u,int v){
	++ncnt;
	ncnt->x=v;
	ncnt->nxt=head[u];
	head[u]=ncnt;	
}
int fa[MAXN],n,m,tag[MAXN],dep[MAXN],siz[MAXN],tot;
int get_fa(int x){
	if(fa[x]==0)
		return x;
	fa[x]=get_fa(fa[x]);
	return fa[x];	
}
pair<int,int> l[MAXN*2];
void dfs(int x,int f){
	dep[x]=dep[f]^1;
	for(node *v=head[x];v!=NULL;v=v->nxt){
		int u=v->x;
		if(u==f)
			continue;
		dfs(u,x);
	}
}
ll cnt[2];
int main(){
	int u,v;
	SF("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		siz[i]=1;
	for(int i=1;i<=m;i++){
		SF("%d%d",&u,&v);
		if(get_fa(u)!=get_fa(v)){	
			add_edge(u,v);
			add_edge(v,u);
			u=get_fa(u);
			v=get_fa(v);
			fa[u]=v;
			siz[v]+=siz[u];
		}
		else
			l[++tot]=make_pair(u,v);
	}
	for(int i=1;i<=n;i++)
		if(fa[i]==0)
			dfs(i,0);
	for(int i=1;i<=tot;i++)
		if(dep[l[i].first]==dep[l[i].second])
			tag[get_fa(l[i].first)]=1;	
	ll ans=0,poi=0;
	for(int i=1;i<=n;i++)
		if(fa[i]==0){
			if(siz[i]==1){
				ans+=n;
				poi++;
			}
			else
				cnt[tag[i]]++;
		}
	ans+=(n-poi)*poi;
	ans+=cnt[1]*(cnt[1]+cnt[0]*2ll);
	ans+=cnt[0]*cnt[0]*2ll;
	PF("%lld",ans);
		
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值