[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;
}