大枝江、Graph Subset Problem
最近最大团的题目出现得越来越多,想到之前模拟赛的大枝江还没订,有点慌。
大枝江
个人站成一排,最开始 站在 处。接下来进行 次操作,每次操作让 和 握手,并交换其位置。求握手关系构成的图的最大团。
突破口:不握手关系按编号顺序构成传递闭包,即 若 没握手、 没握手,则 没握手。
由于最大团 = 补图的最大独立集 = 补图定向后的最小链覆盖,因此答案就是 的拆点二分图最大匹配。(注:代码为了实现方便,是 定向的。)
先来介绍一个更快的匈牙利,在 的补图上跑时,总复杂度是 的。具体就是用并查集维护下一个没有 vis 的点。(我好像不是很会分析匈牙利复杂度……)
复制//事先有:for(int i=1;i<=n;i++)sort(G[i].begin(),G[i].end()); bool dfs(int x){ int k=0; for(int j=find(1);j<x;j=find(j+1)){ while(k<G[x].size()&&G[x][k]<j)k++; if(k<G[x].size()&&G[x][k]==j)continue; fa[j]=j+1; if(!matr[j]||dfs(matr[j])){ matr[j]=x,matl[x]=j; return 1; } } return 0; }
下面考虑怎么实现拆点二分图最大匹配。这个二分图的边数太多了,不能直接跑匈牙利。考虑可以先尽量匹配,即对于每个点,找到其邻居中尚未匹配的任意一个点,匹配上这条边。这与匈牙利最大的不同是,不用每次清空了。这部分代码:
for(int i=1;i<=n;i++)sort(G[i].begin(),G[i].end()); for(int i=1;i<=n+1;i++)fa[i]=i; for(int i=1;i<=n;i++){ int k=0; for(int j=find(1);j<i;j=find(j+1)){ while(k<G[i].size()&&G[i][k]<j)k++; if(k<G[i].size()&&G[i][k]==j)continue; fa[j]=j+1; matl[i]=j; matr[j]=i; ans++; break; } }
那么剩余没有匹配上的左部点数量是多少呢?由于最开始左部点和右部点都是 个,假如现在两边都还剩 个匹配不上,说明不存在任意一条从左边的 个之一连向右边的 个之一的边,而这就有 条边,那么由于这是在补图上考虑,在原图上就对应了这 条边全都有,由于原图只有 条边,所以 。
于是我们可以对剩下的 个点跑匈牙利,这部分复杂度就是 的。
完整代码:
#include <bits/stdc++.h> using namespace std; inline int read(){ int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } const int N=2e5+5; int n,m,ans,aa[N],fa[N],matl[N],matr[N]; vector<int>G[N]; int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); } bool dfs(int x){ int k=0; for(int j=find(1);j<x;j=find(j+1)){ while(k<G[x].size()&&G[x][k]<j)k++; if(k<G[x].size()&&G[x][k]==j)continue; fa[j]=j+1; if(!matr[j]||dfs(matr[j])){ matr[j]=x,matl[x]=j; return 1; } } return 0; } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++)aa[i]=i; for(int i=1,x;i<=m;i++){ x=read(); if(aa[x]>aa[x+1])G[aa[x]].emplace_back(aa[x+1]); else G[aa[x+1]].emplace_back(aa[x]); swap(aa[x],aa[x+1]); } for(int i=1;i<=n;i++)sort(G[i].begin(),G[i].end()); //max clique = butu max duliji = n - butu chaidian bipartite max matching for(int i=1;i<=n+1;i++)fa[i]=i; for(int i=1;i<=n;i++){ int k=0; for(int j=find(1);j<i;j=find(j+1)){ while(k<G[i].size()&&G[i][k]<j)k++; if(k<G[i].size()&&G[i][k]==j)continue; fa[j]=j+1; matl[i]=j; matr[j]=i; ans++; break; } } for(int i=1;i<=n;i++)if(!matl[i]){ for(int j=1;j<=n+1;j++)fa[j]=j; ans+=dfs(i); } cout<<n-ans<<'\n'; }
CF1439B Graph Subset Problem
给定 个点 条边的图,你需要输出一个 个点的团,或者一个 。 的含义是,一个点集,它的导出子图的任意一个点的度数都 。
看到团肯定想根号,团的大小是不可能超过 的。
同时,不论是 个点的团还是 ,每个点的度数都 ,所以先不断删 的点直到不存在。
如果我们打算输出的子集包含 的点,那肯定是团了,就枚举每个 的点 判断其 个邻居是否能与它构成团(用unordered_map存邻接矩阵)。然后将这个点删去。
如果还剩下点,那它们肯定能构成 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具