传送门
首先,如果存在度数小于
k
k
k的点,如果这个点的度数为
k
−
1
k-1
k−1,那么它有机会成为一个大小为
k
k
k的完全子图的一个顶点,于是我们不妨暴力遍历跟这个点相连的所有点,如果这些点的对应的边集的另一个顶点都能找到彼此,那么就找到了完全子图了。可以用
b
i
n
a
r
y
_
s
e
a
r
c
h
binary\_search
binary_search函数快速查找某个元素是否存在于某个有序数组中。如果发现不满足条件,那么这个点肯定不会成为大小为
k
k
k的完全子图的某一个顶点,因此可以将这个点以及它连接的边从整个图中删除。
度数小于 k k k的其它点,既不能成为完全子图,也不能成为有 k k k个邻边的点,也可以删掉。
最后删完了这些不合法的点后,我们就得到了一张度数都大于等于 k k k的图,当然可能这张图上一个点都没有,根据最后的情况来决定如何输出答案即可。
这里分析一下复杂度,对于删边这个操作复杂度是 O ( m ) O(m) O(m)的,可以忽略不计,主要看完全子图的判断这一部分,首先每次判断完全子图对应着会删除 k k k条边,故判断次数总共为 O ( m k ) O(\frac mk) O(km)次,然后每次判断完全子图都是 O ( k 2 l o g n ) O(k^2log n) O(k2logn)的,故总复杂度为 O ( m k l o g ( n ) ) O(mklog(n)) O(mklog(n)),注意完全子图保证了每个点度数为 k − 1 k-1 k−1,事实上 m a x ( k ) = m max(k)=\sqrt m max(k)=m,故总复杂度为 O ( m m l o g ( n ) ) O(m\sqrt mlog(n)) O(mmlog(n)),能够通过本题。
int deg[maxn],vis[maxn];
vector<int>g[maxn],c;
queue<int>q;
int main(){
int t=rd();
while(t--){
int n=rd(),m=rd(),k=rd();
FOR(i,0,m){
int u=rd(),v=rd();
g[u].push_back(v);
g[v].push_back(u);
}
FOR(i,1,n+1){
sort(g[i].begin(),g[i].end());
deg[i]=(int)g[i].size();
if(deg[i]<k)q.push(i),vis[i]=1;
}
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=2;
if(deg[u]==k-1 && c.empty()){
bool ok=true;
c.push_back(u);
FOR(i,0,g[u].size()){
int v=g[u][i];
if(vis[v]==2)continue;
if(deg[v]<k-1){
ok=false;
break;
}
c.push_back(v);
}
if(!ok)c.clear();
FOR(i,0,c.size()){
FOR(j,0,c.size()){
if(i==j)continue;
int u=c[i],v=c[j];
if(!binary_search(g[u].begin(),g[u].end(),v)){
ok=false;
break;
}
}
if(!ok)break;
}
if(!ok)c.clear();
}
FOR(i,0,g[u].size()){
int v=g[u][i];
if(vis[v]>=2)continue;
deg[v]--;
if(deg[v]<k && !vis[v])q.push(v),vis[v]=1;
}
deg[u]=0;
}
int sz=0;
FOR(i,1,n+1)if(deg[i]){
sz++;
}
if(sz){
printf("1 %d\n",sz);
FOR(i,1,n+1)if(deg[i])printf("%d ",i);puts("");
}else if(!c.empty()){
printf("2\n");
FOR(i,0,c.size())printf("%d ",c[i]);puts("");
}else wrn(-1);
c.clear();
FOR(i,1,n+1)g[i].clear(),deg[i]=0,vis[i]=0;
}
}