一.题目链接
https://codeforces.com/contest/1440/problem/D
二.题意
无向图,n个点m条边.再给一个k. (1<=n,m,k<=1e5)
如果可以找到一个点集(这个点集中的每个点的度数都>=k),输出1,并输出方案;否则,如果可以找到大小为k的完全子图(k个点,两两相连),输出2,并输出方案;否则,输出-1.
三.算法分析
输出1的找法
不断将每个度数<k的点和其所连接的边都去掉,如果最后还剩下点,那就可以输出1了.
输出2的找法
不断将每个度数<k-1的点和其所连接的边都去掉,如果在这个过程中有点u的度数为k-1,并且点u和他的k-1个邻居可以构成完全子图,那就保存答案,可以输出2了.
判定这k个点是否为完全子图: 读入后将g[]排序,然后就可以k × k × log k 的查询了.(因为m<1e5,所以k*k/2<1e5,不会超时)
代码优化
初始化vis=0,队列里vis=1,完全去掉以后vis=2.
可以将两个步骤一起进行,将个度数<k的点都放进队列里去,然后一个一个删除,如果要删除的点u恰好满足输出2的条件,那就保存.
要完全去掉点u时,将vis=1->2,并将其相邻点的度数-1,并且更新队列.
四.代码
#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << #x << " = " << x << '\n'
#define ddebug(x , y) cerr << #x << " = " << x << " " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
/*-------------------------------end-----------------------------------------*/
const int maxn=1e5+2000;
int n,m,k,u,v;
vector <int> g[maxn],clique;
queue <int> q;
int d[maxn],vis[maxn];
void init() {
rep(i,1,n) cl(g[i]),d[i]=0,vis[i]=0;
while (!q.empty()) q.pop();
cl(clique);
}
int main() {
// ios;
int T;
cin>>T;
while (T--) {
cin>>n>>m>>k;
init(); // g,d,q,clique,vis
rep(i,1,m) scanf("%d%d",&u,&v),g[u].pb(v),g[v].pb(u);
rep(i,1,n) sort(all(g[i])),d[i]=sz(g[i]);
rep(i,1,n) if (d[i]<k) vis[i]=1,q.push(i);
while (!q.empty()) {
int u=q.front();
q.pop();
vis[u]=2;
if (d[u]==k-1 && clique.empty()) {
clique.pb(u);
for (auto v : g[u]) if (vis[v]!=2) clique.pb(v);
bool flag=1;
for (auto x : clique) for (auto y : clique) if (x!=y && !binary_search(all(g[x]),y)) flag=0;
if (!flag) cl(clique);
}
for (auto v : g[u]) {
d[v]--;
if (!vis[v] && d[v]<k) vis[v]=1,q.push(v);
}
}
int ans=0;
rep(i,1,n) if (!vis[i]) ans++;
if (ans) {
printf("1 %d\n",ans);
rep(i,1,n) if (!vis[i]) printf("%d ",i);
printf("\n");
} else if (!clique.empty()) {
printf("2\n");
for (auto x : clique) printf("%d ",x);
printf("\n");
} else printf("-1\n");
}
}
五.总结
- aoto很好用,代码量少很多.
- 用vis=2表示这个点彻底被删除了,vis=1表示准备处理.