题意
给你n个点的图,一开始没有边,在图中可以设置匹配边(其两个端点都只能连接一条边),一共有m次操作,每次操作加入1条边,求每次操作之后,匹配边总数为1~n/2的方案数。
题解
定义dp[i]表示当前占用点的情况的二进制形式,假如是加边,那么转移方程为
dp[state|f[x]|f[y]]+=dp[state];
假如是删边那么
dp[state|f[x]|f[y]]-=dp[state];
AC代码
#include<stdio.h>
#include<string.h>
#define N 100005
#define P pair<int,int>
using namespace std;
typedef long long ll;
const int M=1e9+7;
ll dp[1200],sum[6],s[1200],ss[1200];
int f[11],n,x,y,d,k;
ll Mod(ll x,ll y)
{
if(x>=y)x-=y;
if(x<0)x+=y;
return x;
}
void dfs(int p,int state)
{
if(p>=n)
{
if(s[state]&1)return ;
k=state|f[x]|f[y];
sum[ss[state]]=Mod(sum[ss[state]]-dp[k],M);
dp[k]=Mod(dp[k]+d*dp[state],M);
sum[ss[state]]=Mod(sum[ss[state]]+dp[k],M);
return;
}
if(p!=x&&p!=y)dfs(p+1,state|f[p]);
dfs(p+1,state);
}
int main()
{
for(int i=0;i<(1<<10);i++)
for(int j=0;j<10;j++)
if(i&(1<<j))
s[i]++;
for(int i=0;i<(1<<10);i++)
ss[i]=s[i]/2+1;
f[0]=1;
for(int i=1;i<=10;i++)
f[i]=f[i-1]*2;
int t,m;
scanf("%d",&t);
for(;t;t--)
{
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
dp[0]=1;
char c[5];
while(m--)
{
scanf("%s%d%d",c,&x,&y);
x--;y--;
d=c[0]=='+'?1:-1;
dfs(0,0);
for(int i=1;i<n/2;i++)
printf("%lld ",sum[i]);
printf("%lld\n",sum[n/2]);
}
}
return 0;
}