题目:
分析:
如果没有:每个被邀请的人都直接认识另外至少d个被邀请的人 这个限制的话,就直接跑tarjan求最大的连通块。
加了这个限制之后,明显有些点是不符合的,我们可以考虑删掉这些点后再跑tarjan。
一个点的入度小于d,就是不满足的,就将与其相连的边都删掉。但这样又会导致与其相连的点因为与它这条边被删去而同样不满足条件,又需要删去。
于是就可以用一个队列保存需要被删的点,是不是很熟悉,没错,就很像拓扑,只是拓扑只将入度为0的入队,而这里将入度小于d的入队
#include<bits/stdc++.h> using namespace std; #define N 200005 int cnt=0,vis[N],T=0,bel[N],low[N],stk[N],num[N],top=0,minn[N],n,d,m; bool fl[N]; int to[N<<1],nex[N<<1],head[N],tot=0,w[N<<1],du[N]; void add(int a,int b){ to[++tot]=b; nex[tot]=head[a]; head[a]=tot; } vector<int>kk[N]; void tarjan(int x) { vis[x]=low[x]=++T; stk[++top]=x; fl[x]=true; for(int i=head[x];i;i=nex[i]){ if(w[i]==-1) continue; int v=to[i]; if(!vis[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(fl[v]) low[x]=min(low[x],vis[v]); } if(vis[x]==low[x]){ cnt++; do{ fl[stk[top]]=false; bel[stk[top]]=cnt; kk[cnt].push_back(stk[top]); num[cnt]++; minn[cnt]=min(minn[cnt],stk[top]); }while(stk[top--]!=x); } } void work() { queue<int>q; for(int i=1;i<=n;i++) if(du[i]<d) q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i;i=nex[i]){ int v=to[i]; if(w[i]==-1) continue; du[v]--; w[i]=-1; if(du[v]<d) q.push(v); } } } int main() { freopen("party.in","r",stdin); freopen("party.out","w",stdout); int a,b; scanf("%d%d%d",&n,&m,&d); for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); du[a]++; du[b]++; } work(); memset(minn,0x7f7f7f,sizeof(minn)); for(int i=1;i<=n;i++) if(!vis[i]) tarjan(i); int ans=0,mn=0x7f7f7f; for(int i=1;i<=cnt;i++) if(num[i]>num[ans]||(num[i]==num[ans]&&minn[i]<mn)) mn=minn[i],ans=i; printf("%d ",num[ans]); for(int i=1;i<=n;i++) if(bel[i]==ans) printf("%d ",i); } /* 18 25 3 9 11 1 15 13 15 3 1 7 16 11 13 17 6 3 13 5 1 7 2 4 15 7 6 11 3 5 10 9 16 6 5 7 1 7 3 17 16 1 7 3 1 5 8 3 9 3 7 1 12 */