[BZOJ]3579: 破冰派对 搜索

Description

由于计算机系的同学们都很宅,很多同学虽然身在一个系,但是入学很久还是相互不认识。学生会主席小Y希望举办一次破冰派对,要让同学们多从寝室里走出来参加娱乐活动,也要让尽量多不认识的同学们通过活动相互认识。自然的,如果参加活动的同学互相都不认识,那便是极好的。?
  要办一次成功的派对是很不容易的,不光需要有同学参加,优秀的工作人员也是必不可少的。他们需要为派对的筹办付出很多的努力,因此一个和谐的团队是非常重要的。小Y希望所有工作人员都是相互认识的。
  计算机系一共有N个同学,所有同学从1到N编号。有M对同学相互认识,而其余的同学相互不认识。
  小Y希望从中选出一些工作人员组成工作团队,让这个工作团队负责活动的组织,而其余的所有非工作人员,就自然都成为了活动的参与者。小Y要求:
  1、工作团队的成员必须相互认识;
  2、参与活动的同学必须相互不认识;
  3、至少有一个同学参与活动,也至少有一个同学是工作人员。
  小Y想知道,一共有多少种工作团队的选择方案呢?

Solution

这题一开始分析了一下,觉得方案好像不是很多,因为限制很多,但是看到有个模数,以为会很多,但其实这个模数是假的……
而且单独求最大团是个NPC问题,是只能搜的,现在这题加了个限制,其实也只能搜。但是我比较辣鸡没敢写。
说一下剪枝吧。一开始先把一些必须工作或参与活动的人选出来,然后把这些已经确定的点删掉,得到很多连通块,最多只有一个是多于一个点的,否则无解(这个显然),只用在那个里面搜就行了。如果全部连通块都是一个点的,那么方案数就是连通块数 + 1 +1 +1

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=1010;
const int mod=1000003;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,ans,tmp[Maxn],lt;bool mark[Maxn];
bitset<Maxn>f[Maxn],c[2],z;
struct Edge{int y,next;}e[Maxn*Maxn<<1];
int last[Maxn],len;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
int p[Maxn];//-1不确定 0工作 1活动
void dfs(int x)
{
    if(x==n+1){if(c[1]!=z&&c[0]!=z)ans++;if(ans>=mod)ans-=mod;return;}
    if(p[x]!=-1){dfs(x+1);return;}
    if((f[x]&c[0])==c[0])p[x]=0,c[0].set(x),dfs(x+1),p[x]=-1,c[0].set(x,0);
    if((f[x]&c[1])==z)
    {
        for(int i=last[x];i;i=e[i].next)if((f[e[i].y]&c[0])!=c[0])return;
        for(int i=last[x];i;i=e[i].next)p[e[i].y]=0,c[0].set(e[i].y);
        p[x]=1,c[1].set(x);
        dfs(x+1);
        for(int i=last[x];i;i=e[i].next)p[e[i].y]=-1,c[0].set(e[i].y,0);
        p[x]=-1,c[1].set(x,0);
    }
}
int main()
{
    int T=read();z.reset();
    while(T--)
    {
        n=read(),m=read();
        if(!m)
        {
            if(n==1)puts("0");
            else printf("%d\n",n);
            continue;
        }
        memset(last,0,sizeof(last));len=ans=0;
        memset(p,-1,sizeof(p));memset(mark,false,sizeof(mark));
        c[0].reset(),c[1].reset();
        for(int i=1;i<=n;i++)f[i].reset();
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            ins(x,y),ins(y,x);f[x].set(y);f[y].set(x);
        }
        for(int i=1;i<=n;i++)f[i].set(i);
        bool no=false;
        for(int x=1;x<=n;x++)
        {
            bool flag=false;
            for(int i=last[x];i;i=e[i].next)
            {
                int y=e[i].y;
                if((f[y]&f[x])!=f[x]){flag=true;break;}
            }
            if(flag)
			{
				if((f[x]&c[0])!=c[0]){no=true;break;}
				p[x]=0,c[0].set(x),mark[x]=true;
			}
        }
        if(no){puts("0");continue;}
        for(int x=1;x<=n;x++)
        if(p[x]==-1&&(f[x]&c[0])!=c[0])
		{
			p[x]=1;
			if((f[x]&c[1])!=z){no=true;break;}
			c[1].set(x),mark[x]=true;
			for(int i=last[x];i;i=e[i].next)
			{
				int y=e[i].y;
				if(p[y]==1){no=true;break;}
				if((f[y]&c[0])!=c[0]){no=true;break;}
				p[y]=0;mark[y]=true;
			}
			if(no)break;
		}
		if(no){puts("0");continue;}
		lt=0;int c0=0;bool all=true;
		for(int x=1;x<=n;x++)
		{
			if(p[x]==0)c0++;
			if(mark[x])continue;
			int cnt=0;
			for(int i=last[x];i;i=e[i].next)
			{
				int y=e[i].y;
				if(!mark[y])cnt++;mark[y]=true;
			}
			if(cnt)all=false;++lt;
		}
		if(all){printf("%d\n",lt+(c0!=0));continue;}
        dfs(1);
        printf("%d\n",ans);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值