1439B Graph Subset Problem(求大小为k的团)

1439B Graph Subset Problem(求大小为k的团)

Codeforces Round #684 (Div. 2)

D. Graph Subset Problem

题面Graph Subset Problem

题意:有一张 n n n 个点 m m m 条边的图,给一个整数 k k k,问图中是否存在大小为 k k k 的团,或者是否存在一个点集,该点集中的每个点都至少有 k k k 个邻点在该点集中。如果两者都存在,输出任意一个,否则输出 -1。

范围 1 ≤ t ≤ 1 e 5 ,   1 ≤ n , m , k ≤ 1 e 5 ,   k ≤ n 1\le t \le 1e5,~1 \le n, m,k \le 1e5,~k \le n 1t1e5, 1n,m,k1e5, kn

保证不存在自环和重边,所有 case 的 n n n 的总和与 m m m 的总和均不超过 2 e 5 2e5 2e5

分析: 显然大小为 k k k 的团中边的数量为 k ( k − 1 ) 2 \frac{k(k-1)}{2} 2k(k1),小于 g o o d   s u b s e t good~subset good subset 所需要的边数,因此若 k ( k − 1 ) 2 > m \frac{k(k-1)}{2} > m 2k(k1)>m,那么答案一定不存在,直接返回 -1。

根据上面的分析可以将该问题中 k k k 的范围从 1 e 5 1e5 1e5 降低为 2 m \sqrt{2m} 2m

由于需要考虑每个点的邻点数量,容易想到从点的度入手,假设当前的图中度最小的点为 u u u,那么有三种情况:

d e g [ u ] ≥ k deg[u] \ge k deg[u]k,此时图中剩下的点集必然形成 g o o d   s u b s e t good~subset good subset

d e g [ u ] < k − 1 deg[u] < k - 1 deg[u]<k1,该点不可能成为 大小为 k k k 的团 或者 g o o d   s u b s e t good~subset good subset 中的点,直接将该点以及与该点相连的边删除,更新邻点的 d e g deg deg,继续取出图中度最小的点进行判断。

d e g [ u ] = = k − 1 deg[u] == k-1 deg[u]==k1,考虑该点以及其邻点所形成的点集是否为大小为 k k k 的团,这里可以暴力对该点集中的点进行两两枚举,例如 a a a b b b,通过二分(使用 unordered_map 更优)来判断 a a a 的邻点中是否存在 b b b,如果使用二分的话需要先对每个点的邻点进行排序,如果都满足条件说明找到了大小为 k k k 的团,输出答案。

复杂度分析:显然复杂度瓶颈在于判断图中是否存在大小为 k k k 的团,由于边数限制,满足 d e g [ u ] = = k − 1 deg[u] == k - 1 deg[u]==k1 的点不会超过 m k − 1 \frac{m}{k-1} k1m 个,进行一次暴力判断需要 O ( k 2 l o g n ) O(k^2logn) O(k2logn),因此总体时间复杂度为 O ( m k ∗ k 2 ∗ l o g n ) = O ( m k l o g n ) = O ( m m l o g n ) O(\frac{m}{k} * k^2*logn) = O(mklogn) = O(m\sqrt{m}logn) O(kmk2logn)=O(mklogn)=O(mm logn),若使用 unordered_map 则复杂度为 O ( m m ) O(m\sqrt{m}) O(mm )

Code

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
 
inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
 
const int MAXN = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
 
int n, m, k;
 
vector<int> E[MAXN], temp;

int vis[MAXN], inEdge[MAXN];

struct Node
{
    int deg, idx;
    bool operator < (const Node &other) const 
    {
        if (deg == other.deg) return idx < other.idx;
        else return deg < other.deg;
    }
};

set<Node> S;

void solve()
{
    while (!S.empty())
    {
        Node node = *S.begin();
        vis[node.idx] = 1;
        if (node.deg >= k)
        {
            cout << 1 << " " << S.size() << endl;
            for (auto it = S.begin(); it != S.end(); it++)
            {
                if (it != S.begin()) cout << " ";
                cout << (*it).idx;
            }
            cout << endl;
            return;
        }
        else
        {
            if (node.deg == k - 1)
            {
                temp.clear();
                int u = node.idx;
                temp.push_back(u);
                for (auto v : E[u])
                {
                    if (vis[v]) continue;
                    temp.push_back(v);
                }
                int res = 1;
                for (auto v : temp)
                {
                    for (auto t : temp)
                    {
                        if (v == t) continue;
                        auto pos = lower_bound(E[v].begin(), E[v].end(), t);
                        if (pos == E[v].end() || *pos != t)
                        {
                            res = 0;
                            break;
                        }
                    }
                    if (!res) break;
                }
                if (res)
                {
                    cout << 2 << endl;
                    for (int i = 0; i < temp.size(); i++)
                    {
                        if (i) cout << " ";
                        cout << temp[i];
                    }
                    cout << endl;
                    return;
                }
            }
            S.erase(S.begin());
            for (auto v : E[node.idx])
            {
                if (vis[v]) continue;
                S.erase({inEdge[v], v});
                S.insert({--inEdge[v], v});
            }
        }
    }
    cout << -1 << endl;
}

signed main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    int T = read();
    while (T--)
    {
        S.clear();
        n = read(), m = read(), k = read();
        for (int i = 0; i <= n; i++)
        {
            vis[i] = inEdge[i] = 0;
            E[i].clear();
        }
        for (int i = 0; i < m; i++)
        {
            int u = read(), v = read();
            E[u].push_back(v);
            E[v].push_back(u);
            inEdge[u]++, inEdge[v]++;
        }
        if (k * (k - 1) > 2 * m)
        {
            cout << -1 << endl;
            continue;
        }
        for (int i = 1; i <= n; i++)
        {
            S.insert({inEdge[i], i});
            sort(E[i].begin(), E[i].end());
        }
        solve();
    }
    return 0;
}

【END】感谢观看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值