[luogu6575]Friends
记s=p+q,当存在一个点度数\ge s时,显然无解
记d_{S,T}=\sum_{x\in S,y\in T}[(x,y)\in E],称S\subseteq V合法当且仅当|S|\le p且d(S,V\backslash S)\le q
结论:若S和T合法,则S\backslash T和T\backslash S中存在一个合法
设\begin{cases}A=S\backslash T,B=T\backslash S\\C=S\cap T,D=V\backslash (S\cup T)\end{cases},则\begin{cases}d(A\cup C,B\cup D)\le q\\d(B\cup C,A\cup D)\le q\end{cases}
反证法,假设两者均不合法,显然大小均\le p,即\begin{cases}d(A,B\cup C\cup D)>q\\d(B,A\cup C\cup D)>q\end{cases}
将上项两对式子者分别相加,即
d(A\cup C,B\cup D)+d(B\cup C,A\cup D)\le 2q<d(A,B\cup C\cup D)+d(B,A\cup C\cup D)将其按"分配律"展开后,可得d(C,D)<0,矛盾
换言之,仅需对每个点找出一个包含其的合法集合,并通过上述方式消除重复元素即可
关于找合法集合,以该点为作为S,并不断决策某个与S相邻的点是否加入S
注意到每次决策后,至少使|S|或d(S,V\backslash S)增加1,因此至多决策s次
用set维护这些待决策点(超过s个即退出),每次决策后需要O(s\log s)处理影响
关于消除重复元素,至多O(ns)次,每次可以O(n)找出一对并O(s^{2})消除
综上,时间复杂度为O(ns2^{s}\log s+n^{2}s+ns^{3}),且跑不满,可以通过
#include<bits/stdc++.h>
using namespace std;
const int N=2505;
int n,m,p,q,x,y,bl[N],d[N],vis[N][N];
vector<int>v0,e[N],v[N];set<int>S;
bool dfs(int s1,int s2){
if ((s1>p)||(s2>q))return 0;
if (S.empty())return 1;
int x=(*S.begin()),s=d[x];
d[x]=-2,S.erase(x);
if (dfs(s1,s2+s))return 1;
d[x]=-1;
for(int j:e[x]){
if (d[j]==-2)s2++;
if ((d[j]>=0)&&(++d[j]==1))S.insert(j);
}
if (dfs(s1+1,s2))return 1;
d[x]=s,S.insert(x);
for(int j:e[x])
if ((d[j]>0)&&(--d[j]==0))S.erase(j);
return 0;
}
int main(){
scanf("%d%d%d",&n,&p,&q);
for(int i=1;i<=n;i++){
scanf("%d",&x);
for(int j=1;j<=x;j++){
scanf("%d",&y);
vis[i][y+1]=1,e[i].push_back(y+1);
}
}
for(int i=1;i<=n;i++){
if (e[i].size()>=p+q){
puts("detention");
return 0;
}
for(int j=i+1;j<=n;j++)
if (vis[i][j]^vis[j][i]){
puts("detention");
return 0;
}
}
for(int i=1;i<=n;i++)
if (!bl[i]){
memset(d,0,sizeof(d));
d[i]=-1,S.clear();
for(int j:e[i])d[j]=1,S.insert(j);
if (!dfs(1,0)){
puts("detention");
return 0;
}
m++;
for(int j=1;j<=n;j++)
if (d[j]==-1){
bl[j]=1;
v[m].push_back(j);
}
}
while (1){
x=y=0;
memset(bl,0,sizeof(bl));
for(int i=1;i<=m;i++){
for(int j:v[i]){
if (!bl[j])bl[j]=i;
else{
x=i,y=bl[j];
break;
}
}
if ((x)&&(y))break;
}
if ((!x)&&(!y))break;
int cnt=0;
memset(bl,0,sizeof(bl));
for(int i:v[x])bl[i]|=1;
for(int i:v[y])bl[i]|=2;
for(int i=1;i<=n;i++)
if (bl[i]==1){
for(int j:e[i])
if ((bl[j]!=1)&&(++cnt>q))break;
if (cnt>q)break;
}
if (cnt<=q){
v[x].clear();
for(int i=1;i<=n;i++)
if (bl[i]==1)v[x].push_back(i);
}
else{
v[y].clear();
for(int i=1;i<=n;i++)
if (bl[i]==2)v[y].push_back(i);
}
}
puts("home");
int cnt=0;
for(int i=1;i<=m;i++)
if (!v[i].empty())cnt++;
printf("%d\n",cnt);
for(int i=1;i<=m;i++)
if (!v[i].empty()){
printf("%d ",v[i].size());
for(int j:v[i])printf("%d ",j-1);
printf("\n");
}
return 0;
}
分类:
luogu
标签:
STL-set
, 基础算法-搜索&剪枝
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 千万级的大表,如何做性能调优?
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· .NET周刊【1月第1期 2025-01-05】
2020-10-21 [luogu6860]象棋与马
2020-10-21 [atACL001F]Center Rearranging