并不对劲的loj3113:p5360:[SDOI2019]热闹的聚会与尴尬的聚会
题目大意
有个\(n\)个点\(m\)条边的图。
要找到一个最小度数为\(p\)的子图和\(q\)个互不相连的点,使\(p\geq\lfloor\frac{n}{q+1}\rfloor\)且\(q\geq\lfloor\frac{n}{p+1}\rfloor\)。
\(n\leq 10^4;m\leq 10^5;数据组数\leq32;\)
题解
题目的条件中把分母乘过去,会发现需要\(p\times q\)尽可能大。
\(p,q\)的大小看上去没什么关系,就相当于让它们分别尽可能大。
使\(p\)尽可能大:发现只有删掉当前的图中度数最小的点,才能使最小度数增加。所以每次删掉当前图中度数最小的点,并更新度数,记下最大的最小度数出现在第几次。
使\(q\)尽可能大:“最大点独立集”是个np问题,但这题没必要“最大”,只要大于\(\lfloor\frac{n}{p+1}\rfloor\)就行。可以每次选出度数最小的点加入点集,删去与它相连的点。如果删去的点数(即度数最小的点的度数)大于\(p\),那么删之前所有点的度数都大于\(p\),那么\(p\)就不是最大的了。之前第一问的做法保证了\(p\)最大,所以每次删的点数不大于\(p\),加入点集的点数\(q\times (p+1)\geq n\)。
这两问中用可删堆容易超时,发现两问中都是一个点只会被删一次,开数组记录已经被删的点就行了。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define maxn 10007
#define maxm 200007
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar(' ');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar(' ');
return;
}
int t,n,m,fir[maxn],nxt[maxm],v[maxm],cnte,ord[maxn],vis[maxn],d[maxn],tp,pos,mx;
priority_queue<pii >q;
void ade(int u1,int v1){v[cnte]=v1,nxt[cnte]=fir[u1],fir[u1]=cnte++;}
int main()
{
t=read();
while(t--)
{
n=read(),m=read();
rep(i,1,n)fir[i]=-1,d[i]=ord[i]=vis[i]=0;cnte=mx=pos=tp=0;
rep(i,1,m){int x=read(),y=read();ade(x,y),ade(y,x),d[x]++,d[y]++;}
rep(i,1,n)q.push(mp(-d[i],i));
while(!q.empty())
{
int u=q.top().se,su=-q.top().fi;q.pop();if(vis[u])continue;
vis[u]=1;ord[++tp]=u;if(!pos||mx<su)pos=tp,mx=su;
view(u,k)if(!vis[v[k]]){d[v[k]]--;q.push(mp(-d[v[k]],v[k]));}
}
write(n-pos+1);rep(i,pos,n)write(ord[i]);puts("");
tp=0;rep(i,1,n)q.push(mp(-d[i],i)),vis[i]=0;
while(!q.empty())
{
int u=q.top().se;q.pop();if(vis[u])continue;
vis[u]=1;ord[++tp]=u;
view(u,k)vis[v[k]]=1;
}
write(tp);rep(i,1,tp)write(ord[i]);puts("");
}
return 0;
}
奇怪的思路
把每个点编为与周围染好的点不同的编号最小的编号。
第一问取编号恰为1~最大编号的点,第二问取数量最多的同一编号的点(数量一定大于\(\lfloor\frac{n}{编号数+1}\rfloor\))。
但是最大编号和进行编号的顺序有关,有的图能通过不巧的编号顺序增大最大编号,所以这个方法是错的(或者说我不知道能不能修)。
这个方法只有10分,第一问无脑输出所有点(loj数据)都有80分。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· 趁着过年的时候手搓了一个低代码框架
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现