Kattis

From : North American Invitational Programming Contest 2018

给你一个图,以及它的补图。如果部分点在原图中是团,并且其他的所有点在补图中也是团,那么就叫做一个双团。

要求计算图中双团的数量。

这篇博客使我理解了这个问题:zro  https://www.cnblogs.com/clrs97/p/8730429.html  orz

如果其他点在补图中构成团,那么这些点在原图中是一个独立集。

可以想到,满足条件的状态是 (团的点数) × (团的点数 - 1) + 独立集点的度数和 =  团点的度数和。

因为 (团的点数) × (团的点数 - 1) = 团中所有边构成的度数,所以剩下的度数就是团的点 与 独立集的点之间的边所构成的度数了。

这样我们可以找出一种可行方案。

那么剩下的方案,可能是

1:团中一个点加入到独立集中:

2:也可能是独立集中一个点加入到团中

3:1+2

分别统计答案即可。

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 2*1e5 + 10;
const LL M = 1e9+7;

int d[maxn], sum[maxn];


int main()
{
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++)
        {
                int x, y;
                scanf("%d%d", &x, &y);
                d[x]++, d[y]++;
        }
        sort(d+1, d+1+n);
        reverse(d+1, d+1+n);

        for (int i = 1; i <= n; i++)
                sum[i] = sum[i-1] + d[i];

        LL ans = 0;
        int mid = 0;
        for (int i = 0; i <= n; i++)
                if (1ll*i*(i-1)+sum[n]-sum[i] == sum[i])
                {
                        ans++;
                        mid = i;
                        break;
                }
                
        //找到一种可行方案。
        //团中的点的度数一定大于等于独立集中的点的度数,所以排个序直接枚举断点即可。

        if (!ans)
        {
                printf("0
");
                return 0;
        }
        for (int i = 1; i <= mid; i++)
                if (1ll*(mid-1)*(mid-2)+sum[n]-sum[mid]+d[i] == sum[mid]-d[i]) ans++;
        for (int i = mid+1; i <= n; i++)
                if (1ll*(mid+1)*mid+sum[n]-sum[mid]-d[i] == sum[mid]+d[i]) ans++;

        LL x = 0, y = 0;
        for (int i = 1; i <=mid; i++)
                if (d[i] == d[mid]) x++;
        for (int i = mid+1; i <= n; i++)
                if (d[i] == d[mid]) y++;

        ans = (ans+x*y)%M;
        printf("%lld", ans);
}
原文地址:https://www.cnblogs.com/ruthank/p/9510168.html