[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;
}
posted @ 2022-10-21 19:22  PYWBKTDA  阅读(102)  评论(0编辑  收藏  举报