图论——AtCoder Grand Contest #C Squared Graph

  • 题目链接: http://agc011.contest.atcoder.jp/tasks/agc011_c

  • 题意: 给出一个N个点,M条边的无向图(无自环,重边),现在构造出一个新图,有N*N个点,若在原图中存在边(a,b)和(a’,b’),则新图中存在从点(a,a’)到点(b,b’)的无向边。求新图中的联通分量的个数

  • 分析:
    我们可以将原题转换成:给出2个无向图A和B,当且仅当图A中存在边(a,b),图B中存在边(a’,b’)时,新图中存在点(a,a’)到点(b,b’)的无向边。
    所以,我们可以发现,新图中2个点连接的前提条件下,是这2个点对应的(x,y)坐标在原图中都对应连接。
    那么,我们可以开始计算原图中孤立点的个数:

    1. A图中孤立的点和B图中孤立的点构成新图中的点 (IaIb) 也是孤立的

    2. A图中孤立的点和B图中非孤立的点构成新图中的点 (IaNbIb) 也是孤立的

    3. B图中孤立的点和A图中非孤立的点构成新图中的点 (IbNaIa) 也是孤立的

    处理完孤立的点后,我们再来看剩下的联通分量。每一个联通分量我们可以当做一个‘点’来处理,这样看,似乎它的计算方式和孤立点一样: Pa×Pb 即可。但是如果这个联通分量是二分图的话,根据二分图的性质,情况就不一样了:

    1. 假设原图A和B中都含有一个二分图,且每个二分图分成S集合和T集合,那么以这两个二分图为基础,我们可以在新图中构建出2个联通分量: (SASB)(TATB) (SATB)(TASA)

    2. 所以我们需要单独计算出二分图联通分量的个数 Q 和非二分图联通分量的个数P

    3. 那么这些原图中联通分量构成新图中联通分量的为: (PAPB) (PAQB) (PBQA) 2(QAQB)

    所以最终结果为 :
    (IA×IB+IA×(NBIB)+IB×(NAIA))+PA×PB+PA×QB+QA×PB+2×QA×QB
    并且算法复杂度为二分图判定的时间复杂度,即等同于遍历了所有边,即 O(M)

  • AC 代码:

/*************************************************************************
> File Name: test.cpp
> Author: Akira
> Mail: qaq.febr2.qaq@gmail.com
************************************************************************/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#include<cstring>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 100010
#define MaxM MaxN*20
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
const int mod = 1E9 + 7;
const double eps = 1e-6;
#define bug cout<<88888888<<endl;
#define debug(x) cout << #x" = " << x;
int N, M;
struct Edge
{
    int u, v, next;
}edge[MaxM];
int cont;
int head[MaxN];
int vis[MaxN];
int col[MaxN];
void add(int u, int v)
{
    edge[cont].u = u;
    edge[cont].v = v;
    edge[cont].next = head[u];
    head[u] = cont++;
}
void Add(int u, int v) { add(u, v); add(v, u); }
void init()
{
    cont = 0;
    MST(head, -1);
    CLR(vis);
}
int main()
{
    //std::ios::sync_with_stdio(false);
    while (~scanf("%d%d", &N, &M))
    {
        init();
        while (M--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            Add(u, v);
        }
        LL I = 0, Q = 0, P = 0;
        for (int i = 1; i<=N; i++)
        {
            if(!vis[i]) 
            {
                bool flag = true;
                queue<int> q;
                vector<int> V;
                q.push(i);
                V.push_back(i);
                vis[i] = 1;
                col[i] = 0;
                while(!q.empty())
                {
                    int u = q.front();
                    q.pop();
                    for(int j=head[u];j!=-1;j=edge[j].next)
                    {
                        int v = edge[j].v;
                        if(!vis[v])
                        {
                            vis[v] = 1;
                            q.push(v);
                            V.push_back(v);
                            col[v] = col[u] + 1;
                        }
                        else
                        {
                            if( (col[v]-col[u])%2 == 0) flag = false;
                        }
                    }
                }               

                if (V.size() == 1) I++;
                else
                {
                    if (flag) Q++;
                    else P++;
                }
            }
        }
        //cout << I << " " << Q << " " << P << endl;
        LL ans = 2*I*N - I*I + P*P + 2 * P*Q + 2*Q*Q;
        printf("%lld\n", ans);
    }
    //system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值