[bzoj3579][乱搞]破冰派对

55 篇文章 0 订阅

Description

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

Input

第一行读入一个整数T,表示测试数据的组数。接下来T组数据,每组数据格式如下:   第一行包含两个整数N和M。
  接下来M行,第i行包含两个不同的,在1到N之间的整数xi,yi,表示编号为xi和yi的同学相互认识。
  输入数据保证,在每一组测试数据中,任意两个同学之间的朋友关系都不会被列出两次。

Output

对于每一组测试数据输出一行一个整数,表示可行的方案总数,模1000003的余数。

Sample Input

2

1 0

4 4

1 2

1 3

2 3

3 4

Sample Output

0

3

HINT

对于100%的数据满足1 ≤ N ≤ 1000,0 ≤ M ≤ N^2,1 ≤ T ≤ 6。

题解

这不是一个搜索题
题目让你把他分成两个块,满足一个块之间两两有边而另一个块之间两两无边,问方案数
如果我们能弄出来一个合法解,可以知道,剩余的解只有可能有三种情况
1:在未选择的图选择一个加入
2:在选择的图中选择一个拿出
3:在未选择的图中选择一个拿入并在选择的图中选择一个拿出
证明是显然的
然后我们考虑如何弄出一个合法解
把点按度数从大到小排序,这里需要一个结论就是,如果一个度数大的点没有在选择的图中,那么剩余度数比他小的点全部都不能在选择的图中
因为一个点如果没选,那么与之相连的全部都要被选中,又知道剩余加入的点一定要至少与这些点有边,但是他们的度数比我当前这个度数小,所以不可能再被加入了
那么从大到小加入点直到做出一个合法的方案,剩余按上面调整即可
复杂度 O ( m + n l o g n ) O(m+nlogn) O(m+nlogn)
这个题其实允许了 n 2 n^2 n2,那么其实还可以 2 − s a t 2-sat 2sat直接 n 2 n^2 n2暴力建边求一个合法解

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=1005;
struct edge{int x,y,next;}a[MAXN*MAXN*2];int len,last[MAXN];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
int n,m,cal[MAXN],du[MAXN],fac[MAXN];
bool cmp(int n1,int n2){return du[n1]>du[n2];}
vector<int> A,B;
bool in[MAXN];
int vi[MAXN],tim;
int main()
{
//	freopen("a.in","r",stdin);
	int CASE=read();
	while(CASE--)
	{
		len=0;memset(last,0,sizeof(last));
		memset(du,0,sizeof(du));A.clear();
		memset(in,false,sizeof(in));memset(vi,0,sizeof(vi));tim=0;
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			int x=read(),y=read();
			ins(x,y);ins(y,x);
			du[x]++;du[y]++;
		}
		for(int i=1;i<=n;i++)fac[i]=i;
		int lin=m;
		sort(fac+1,fac+1+n,cmp);
		int cnt=0;bool ok=true;
		for(int i=1;i<=n;i++)
		{
			int sum=0;
			for(int k=last[fac[i]];k;k=a[k].next)
			{
				int y=a[k].y;
				if(!in[y])m--;
				else sum++;
			}
			if(sum==cnt)A.push_back(fac[i]),in[fac[i]]=true,cnt++;
			else if(m)break;
		}
		if(m){puts("0");continue;}
		LL as=0,plan=1;
		for(int i=1;i<=n;i++)if(in[i])as+=cal[i];
		LL total=as;
		if(cnt==n)plan=0;
	//	if(!lin)as=max(as,0LL),plan++;
		for(int i=1;i<=n;i++)if(!in[i])
		{
			int sum=0;tim++;
			for(int k=last[i];k;k=a[k].next)
			{
				int y=a[k].y;
				if(in[y])sum++,vi[y]=tim;
			}
			if(sum==cnt-1)
			{
				int u;
				for(int j=0;j<A.size();j++)if(vi[A[j]]!=tim){u=A[j];break;}
				as=max(as,total-cal[u]+cal[i]);plan++;
			}
			else if(sum==cnt&&cnt!=n-1)as=max(as,total+cal[i]),plan++;
		}
		for(int i=1;i<=n;i++)if(in[i])
		{
			bool tf=true;
			for(int k=last[i];k;k=a[k].next)
			{
				int y=a[k].y;
				if(!in[y]){tf=false;break;}
			}
			if(tf&&cnt!=1)as=max(as,total-cal[i]),plan++;
		}
	//	if(CASE&1)
		pr2(plan%1000003);
	}
//	else pr2(as);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值