把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5361】[SDOI2019] 热闹的聚会与尴尬的聚会(联合构造题)

点此看题面

  • 给定一张\(n\)个点\(m\)条边的图。
  • 要求构造两个子图(可以有重复点),第一个子图中有任意多个点且所有点度数大于等于\(p\),第二个子图有\(q\)个点且是一个独立集。
  • \(p,q\)由你自己决定,只需满足\(\lfloor\frac n{p+1}\rfloor\le q\)\(\lfloor\frac n{q+1}\rfloor\le p\)
  • 数据组数\(\le32\)\(n\le10^4,m\le10^5\)

联合构造题

要单独构造一个最大独立集显然是不太可能的事。

分析\(\lfloor\frac n{p+1}\rfloor\le q\Leftarrow\frac n{p+1}<q+1\Leftrightarrow(p+1)(q+1)>n\),而另一个条件也同样可以转化成这个式子。

那么,就容易想到把这两个东西合起来一起构造。

具体地,对于第一个子图,我们可以直接枚举最小的度数\(p\),那么所有比这个度数小的点都将被删掉。

每删掉一个点,我们都把它尝试加入独立集,由于这个点度数肯定小于最终的\(p\),即便它被加入了独立集,也只会让小于\(p\)个数无法再加入独立集。

这么一看,显然可以满足\((p+1)(q+1)>n\),于是这道题就做完了。


感谢 qwqqqqq 的指出,当删去一条边之后对应点度数可能会小于 \(p\),因此需要将 \(p\) 向这个点度数取 \(\min\)。(不知道以前是怎么过的,可能加强数据了吧)

代码:\(O(Tm)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10000
#define M 100000
#define Pr pair<int,int>
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,A[N+5],B[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[2*M+5];
int d[N+5],nB[N+5],vis[N+5];queue<int> q[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc(' ');}
	I void writeln() {pc('\n');}
}using namespace FastIO;
int main()
{
	RI Tt,i,x,y,k,p,a,b,ct;read(Tt);W(Tt--)
	{
		for(read(n,m),ee=0,i=1;i<=n;++i) {lnk[i]=d[i]=nB[i]=0,vis[i]=-1;W(!q[i].empty()) q[i].pop();}//清空
		for(i=1;i<=m;++i) read(x,y),add(x,y),add(y,x),++d[x],++d[y];//初始图,统计每个点度数
		for(i=1;i<=n;++i) q[d[i]].push(i);for(a=b=p=0;p<=n&&(p+1)*(b+1)<=n;++p) W(!q[p].empty())//枚举最小度数,然后删去最小度数的点
		{
			if(k=q[p].front(),q[p].pop(),~vis[k]) continue;vis[k]=p,!nB[k]&&(B[++b]=k);//已访问过的删去,然后尝试加入独立集
			for(i=lnk[k];i;i=e[i].nxt) !~vis[e[i].to]&&(!nB[k]&&(nB[e[i].to]=k),q[--d[e[i].to]].push(e[i].to),p=min(p,d[e[i].to]));//更新相连点度数,如果k在独立集中还要让相连点无法进入
		}
		for(i=1;i<=n;++i) !~vis[i]&&(A[++a]=i);//没被访问到的点度数≥最小度数,作为第一子图中的点
		for(write(a),i=1;i<=a;++i) write(A[i]);writeln();for(write(b),i=1;i<=b;++i) write(B[i]);writeln();//输出构造方案
	}return clear(),0;
}
posted @ 2021-03-31 07:08  TheLostWeak  阅读(45)  评论(3编辑  收藏  举报