AtCoder Grand Contest 011 C - Squared Graph 乱搞

题意

有一个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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值