Codeforces Round #684 (Div. 2) Graph Subset Problem

题目链接
You are given an undirected graph with n n n vertices and m m m edges. Also, you are given an integer k k k.

Find either a clique of size k k k or a non-empty subset of vertices such that each vertex of this subset has at least k k k neighbors in the subset. If there are no such cliques and subsets report about it.

A subset of vertices is called a clique of size k k k if its size is k k k and there exists an edge between every two vertices from the subset. A vertex is called a neighbor of the other vertex if there exists an edge between them.

Input
The first line contains a single integer t t t ( 1 ≤ t ≤ 1 0 5 1 \leq t \leq 10^5 1t105) — the number of test cases. The next lines contain descriptions of test cases.

The first line of the description of each test case contains three integers n n n, m m m, k k k ( 1 ≤ n , m , k ≤ 1 0 5 1 \leq n, m, k \leq 10^5 1n,m,k105, k ≤ n k \leq n kn).

Each of the next m m m lines contains two integers u , v u, v u,v ( 1 ≤ u , v ≤ n , u ≠ v ) (1 \leq u, v \leq n, u \neq v) (1u,vn,u=v), denoting an edge between vertices u u u and v v v.

It is guaranteed that there are no self-loops or multiple edges. It is guaranteed that the sum of n n n for all test cases and the sum of m m m for all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105.

Output
For each test case:

If you found a subset of vertices such that each vertex of this subset has at least k k k neighbors in the subset in the first line output 1 1 1 and the size of the subset. On the second line output the vertices of the subset in any order.

If you found a clique of size k k k then in the first line output 2 2 2 and in the second line output the vertices of the clique in any order.

If there are no required subsets and cliques print − 1 -1 1.

If there exists multiple possible answers you can print any of them.

Example
input

3
5 9 4
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
10 15 3
1 2
2 3
3 4
4 5
5 1
1 7
2 8
3 9
4 10
5 6
7 10
10 8
8 6
6 9
9 7
4 5 4
1 2
2 3
3 4
4 1
1 3

output

2
4 1 2 3 
1 10
1 2 3 4 5 6 7 8 9 10 
-1

Note
In the first test case: the subset { 1 , 2 , 3 , 4 } \{1, 2, 3, 4\} {1,2,3,4} is a clique of size 4 4 4.

In the second test case: degree of each vertex in the original graph is at least 3 3 3. So the set of all vertices is a correct answer.

In the third test case: there are no cliques of size 4 4 4 or required subsets, so the answer is − 1 -1 1.
首先,如果 k ⋅ ( k − 1 ) 2 > m \frac{k\cdot(k-1)}{2}>m 2k(k1)>m 则一定无解。
大小为 k k k 的团和度大于等于 k k k 的完全子图只如果存在要找到其中一个即可。
由于因此大小为 k k k 的团在度上的要求更宽松一些,因此不妨先判断是否存在大小为 k k k 的团。
如果一个点在大小为 k k k 的团中,则首先该点的度一定大于等于 k − 1 k-1 k1 ,因此可以通过拓扑排序删掉图中度小于 k − 1 k-1 k1 的点,剩下的点构成的图中每个点的度大于等于 k − 1 k-1 k1
类似拓扑排序,将度等于 k − 1 k-1 k1 (选度最小的,否则会出现条件与后面的操作不相符的情况) 点入队,每次从队列中取出一个元素,若与该点相邻的点度数大于等于 k − 1 k-1 k1 的点数量小于 k − 1 k-1 k1 时,则该点一定不在团中,因此将该点删掉;若与该点相邻的点度数大于等于 k − 1 k-1 k1 的点数量等于 k − 1 k-1 k1 时,枚举相邻的 k − 1 k-1 k1 个点中任意两个点判断是否连通,从而判断出这 k − 1 k-1 k1 个点是否构成完全图。
上面求大小为 k k k 的团可能会落下一种情况,即图中每个点的度数都大于 k − 1 k-1 k1 ,且存在大小为 k k k 的团,而这种框符合每个点的度数至少为 k k k 的完全子图。
因此如果没找出大小为 k k k 的团,则继续找每个点的度数至少为 k k k 的完全子图即可。方法同样类似拓扑排序,将度数小于 k k k 点都删掉,如果剩下点,则找到一个连通的点集即为答案,否则输出 − 1 -1 1

#include<bits/stdc++.h>

using namespace std;
const int N = 1e5 + 10;
int T, n, m, k, deg[N];
bool v[N];
queue<int> q;
vector<int> G[N], tmp;

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &m, &k);
        memset(deg, 0, sizeof(int) * (n + 1));
        for (int i = 1; i <= n; ++i)G[i].clear();
        for (int i = 1; i <= m; ++i) {
            int x, y;
            scanf("%d%d", &x, &y);
            G[x].push_back(y), G[y].push_back(x);
            deg[x]++, deg[y]++;
        }
        if (1ll * k * (k - 1) / 2 > m) {
            puts("-1");
            continue;
        }
        for (int i = 1; i <= n; ++i) {
            sort(G[i].begin(), G[i].end());
            if (deg[i] < k - 1)q.push(i);
        }
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            if (!deg[x])continue;
            deg[x] = 0;
            for (int y:G[x])
                if (deg[y] && --deg[y] < k - 1)q.push(y);
        }
        bool suc = false;
        for (int i = 1; i <= n; ++i)if (deg[i] == k - 1)q.push(i);
        while (!q.empty()) {
            int x = q.front();
            q.pop(), tmp.clear();
            for (int y:G[x])if (deg[y] >= k - 1)tmp.push_back(y);
            if (tmp.size() < k - 1) {
                for (int y:tmp) if (--deg[y] == k - 1)q.push(y);
                deg[x] = 0;
                continue;
            }
            bool flag = true;
            for (int i = 0; i <= k - 3; ++i) {
                for (int j = i + 1; j <= k - 2; ++j)
                    if (!binary_search(G[tmp[i]].begin(), G[tmp[i]].end(), tmp[j])) {
                        flag = false;
                        break;
                    }
                if (!flag)break;
            }
            if (flag) {
                puts("2");
                printf("%d", x);
                for (int y:tmp)
                    printf(" %d", y);
                puts("");
                suc = true;
                while (!q.empty())q.pop();
                break;
            } else {
                for (int y:tmp) if (--deg[y] == k - 1)q.push(y);
                deg[x] = 0;
            }
        }
        if (suc)continue;
        for (int i = 1; i <= n; ++i)if (deg[i] && deg[i] < k)q.push(i);
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            if (!deg[x])continue;
            deg[x] = 0;
            for (int y:G[x])if (deg[y] && --deg[y] < k)q.push(y);
        }
        for (int i = 1; i <= n; ++i)
            if (deg[i] >= k) {
                suc = true;
                tmp.clear();
                q.push(i), v[i] = true;
                while (!q.empty()) {
                    int x = q.front();
                    tmp.push_back(x);
                    q.pop();
                    for (int y:G[x]) {
                        if (v[y] || deg[y] < k)continue;
                        v[y] = true, q.push(y);
                    }
                }
                printf("1 %d\n", tmp.size());
                for (int x:tmp)printf("%d ", x), v[x] = false;
                puts("");
                break;
            }
        if (!suc)puts("-1");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_sky123_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值