Processing math: 3%

[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 pd(S,V\backslash S)\le q

结论:ST合法,则S\backslash TT\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 @   PYWBKTDA  阅读(104)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 我干了两个月的大项目,开源了!
· 推荐一款非常好用的在线 SSH 管理工具
· 千万级的大表,如何做性能调优?
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· .NET周刊【1月第1期 2025-01-05】
历史上的今天:
2020-10-21 [luogu6860]象棋与马
2020-10-21 [atACL001F]Center Rearranging
点击右上角即可分享
微信分享提示