Party

2 聚会(party.c/cpp/pas)
2.1 题目描述
你要精心策划一场聚会。
现在有 n 个人,一共有 m 个认识关系,认识的人一定是相互认识的。由于这次聚会十
分重要,你希望被邀请的每个人都直接或间接认识,不仅如此,为了避免尴尬,你还希望每
个被邀请的人都直接认识另外至少 d 个被邀请的人。作为策划者,你希望使得被邀请的人
最多。注意同一对关系最多只会出现一次,且不会出现自环。
你需要输出被邀请的人数,并且将被邀请的人的编号从小到大输出。为了避免你的重度
选择恐惧症发作,当存在多个答案时,你需要使得被邀请的人的编号越小越好。
2.2 输入格式
第一行为三个整数 nmd,分别表示人数、认识关系数以及题中给出的限制 d。
接下来 m 行 ,每行两个数 ab,表示 ab 相互认识。
2.3 输出格式
第一行一个整数,表示被邀请的人数 k。保证有解,即 k>0。
第二行 k 个整数,从小到大输出你邀请的人的编号。
2.4 样例输入
4 4 2
1 2
2 3
3 4
4 2
2.5 样例输出
NOIP 模拟试题 #5
5
3
2 3 4
2.6 数据范围与约定
对于 20%的数据,n<=18,m<=50
对于 100%的数据,2<=n<=200000, 1<=m<=200000 , 1<=d<n

先找出邻接点

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=210000;
int head[maxn],net[2*maxn],to[2*maxn],cnt;
int dfn[maxn],low[maxn],c[maxn],s[maxn],col,num,top;
int sum[maxn];
int no_r[maxn];
bool flag[maxn],f[maxn];
int ans=0,ans_c=0,d_min=1e9;
void add(int a,int b)
{
    cnt++;
    to[cnt]=b;
    net[cnt]=head[a];
    head[a]=cnt;
}
void dfs(int x)
{
    dfn[x]=++num;
    low[x]=num;
    f[x]=1;
    s[++top]=x;

    for(int i=head[x];i;i=net[i])
    {
        int tmp=to[i];
        if(flag[tmp]) continue;
        if(dfn[tmp]==0)
        {
            dfs(tmp);
            low[x]=min(low[x],low[tmp]);
        }
        else 
        if(f[tmp]==1)
        {
        low[x]=min(low[x],dfn[tmp]);
        }
    } 

    if(low[x]==dfn[x])
    {
        f[x]=0;
        c[x]=++col;
        int min1=x;
        int tot=1;
        while(s[top]!=x)
        {
            tot++;
            min1=min(min1,s[top]);
            c[s[top]]=col;
            f[s[top--]]=0;
        } 
        top--;
        if(tot>ans) ans=tot,ans_c=col;
        if(tot==ans)
         if(min1<d_min) d_min=min1,ans_c=col;
    }
}
int main()
{
    int n,m,d;
    scanf("%d%d%d",&n,&m,&d);
    for(int i=1;i<=m;i++)
     {
        int x,y;
        scanf("%d%d",&x,&y);
        sum[x]++,sum[y]++;
        add(x,y),add(y,x);
     }
    int l=0,r=0;
    for(int i=1;i<=n;i++)
     if(sum[i]<d) no_r[++r]=i,flag[i]=1;
    while(l<=r)
     {
       l++;
       int tmp=no_r[l];
       for(int i=head[tmp];i;i=net[i])
       if(!flag[to[i]])
        {
            sum[to[i]]--;
            if(sum[to[i]]<d) no_r[++r]=to[i],flag[to[i]]=1;
        } 
     }
     for(int i=1;i<=n;i++)
      if(!flag[i]&&!dfn[i])
       dfs(i);
     printf("%d\n",ans);
     for(int i=1;i<=n;i++)
      if(c[i]==ans_c) printf("%d ",i);
     return 0; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值