zoukankan      html  css  js  c++  java
  • 【洛谷P5361】热闹的聚会与尴尬的聚会

    题目

    题目链接:https://www.luogu.com.cn/problem/P5361
    小 Q 的生日快到了,他决定周末邀请一些朋友到他的新房子一起聚会!
    他的联系薄上有 (n) 位好友,他们两两之间或者互相认识,或者互相不认识。小 Q 希望在周六办一个热闹的聚会,再在周日办一个尴尬的聚会。

    • 一场热闹度为 (p) 的聚会请来了任意多位好友,对于每一位到场的好友来说都有至少 (p) 位他认识的好友也参加了聚会,且至少对于一位到场的好友来说现场恰好有 (p) 位他认识的好友;
    • 一场尴尬度为 (q) 的聚会请来了恰好 (q) 位好友,且他们两两互不认识。

    两场聚会可能有重复的参与者,联系薄上也有可能有某些好友同时缺席了两场聚会。
    小 Q 喜欢周六聚会的热闹度 (p) 与周日聚会的尴尬度 (q) 之间满足:(leftlfloor frac{n}{p+1} ight floor! le q)(leftlfloor frac{n}{q+1} ight floor! le p)
    请帮助小 Q 找出一个可行的邀请方案。
    (Qleq 32,nleq 10^4,mleq 10^5)

    思路

    也就是给你一张图,要求选出两个点集(可以有交):

    • 第一个点集没有限制,单独把第一个点集里的点和边拿出来,记度数最小的点的度数为 (p)
    • 第二个点集要求是图的独立集,记点集大小为 (q)

    要求 (leftlfloor frac{n}{p+1} ight floor! le q)(leftlfloor frac{n}{q+1} ight floor! le p),并给出任意一种方案。
    看到独立集自然想到这是一道乱搞题。
    假设目前第一个点集为 (S),我们要删去一个点使得 (p) 更大,那么显然删去度数最小的点最优。然后继续更新其他依然在 (S) 中的点的度数。
    维护一个堆即可。不难发现这样是可以找到最优的 (p) 的。
    考虑最大化 (q),显然这个是没有确定性算法的,所以直接模拟退火!
    其实可以直接不断选择度数最小的点,删去图中与它相连的边。
    因为我们每次选择的点的度数一定不超过 (p),如果超过了,那么第一问中肯定有大于 (p) 的方案。所以每次最多删去 (p+1) 个点,那么就有 (leftlfloor frac{n}{p+1} ight floor! le q)
    时间复杂度 (O(Q(n+m)log n))

    代码

    #include <bits/stdc++.h>
    #define mp make_pair
    #define ST first
    #define ND second
    using namespace std;
    
    const int N=200010;
    int Q,n,m,p,pos,tot,head[N],deg[N],deg1[N],vis1[N];
    bool vis2[N];
    priority_queue<pair<int,int> > q;
    queue<int> q1;
    
    int read()
    {
    	int d=0; char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot; deg[to]++; deg1[to]++;
    }
    
    void prework()
    {
    	while (q.size()) q.pop();
    	while (q1.size()) q1.pop();
    	for (int i=1;i<=n;i++)
    		head[i]=-1,deg[i]=deg1[i]=vis1[i]=vis2[i]=0;
    	tot=p=pos=0;
    }
    
    int main()
    {
    	Q=read();
    	while (Q--)
    	{
    		n=read(); m=read();
    		prework();
    		for (int i=1,x,y;i<=m;i++)
    		{
    			x=read(); y=read();
    			add(x,y); add(y,x);
    		}
    		for (int i=1;i<=n;i++)
    			q.push(mp(-deg[i],i));
    		for (int i=1;i<=n;i++)
    		{
    			while (vis1[q.top().ND]) q.pop();
    			int x=q.top().ND; q.pop();
    			vis1[x]=i;
    			if (deg[x]>=p) p=deg[x],pos=i;
    			for (int i=head[x];~i;i=e[i].next)
    			{
    				int v=e[i].to;
    				if (!vis1[v]) q.push(mp(-(--deg[v]),v));
    			}
    		}
    		while (q.size()) q.pop();
    		for (int i=1;i<=n;i++)
    			deg[i]=deg1[i],q.push(mp(-deg[i],i));
    		while (q.size())
    		{
    			while (q.size() && vis2[q.top().ND]) q.pop();
    			if (!q.size()) break;
    			int x=q.top().ND; q.pop();
    			vis2[x]=1; q1.push(x);
    			for (int i=head[x];~i;i=e[i].next)
    				vis2[e[i].to]=1;
    		}
    		tot=0;
    		for (int i=1;i<=n;i++)
    			if (vis1[i]>=pos) tot++;
    		cout<<tot;
    		for (int i=1;i<=n;i++)
    			if (vis1[i]>=pos) cout<<' '<<i;
    		cout<<"
    ";
    		cout<<q1.size();
    		for (;q1.size();q1.pop())
    			cout<<' '<<q1.front();
    		cout<<"
    ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java设计模式——单例模式
    Java设计模式——工厂模式
    多线程
    Collection集合
    内部类
    多态
    接口
    面向对象(2)
    数组
    面向对象(1)
  • 原文地址:https://www.cnblogs.com/stoorz/p/14826333.html
Copyright © 2011-2022 走看看