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 2−sat直接 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;
}