题意:给出n个点,q次操作,每次增加一个边,或删除一个边。
问你每次操作后图中的匹配数为k的方案有多少种。
思路:看到n=10,可以想到暴力枚举每个状态。因为操作数右30000种,可以想到状态转换,所以可以使用状压dp。
要预先处理好每个数字含有的1的个数,还有含有偶数个1的点要预先存储一下。不然会T,然后我们根据每个操作后所含的点来转移状态。
#include <bits/stdc++.h>
using namespace std;
int dp[2][2005];
const int mod=1e9+7;
void add(int &x, int y)
{
x += y;
if(x >= mod) x -= mod;
}
void sub(int &x, int y)
{
x -= y;
if(x < 0) x += mod;
}
int ans[6];
int bi[2005];
int sta[2005];
int num[2005];
int main()
{
int cnt=0;
for(int i=0; i<1024; ++i)
{
num[i]= num[i>>1]+(i&1);//i的二进制有几个1
if(~num[i]&1) sta[cnt++] = i;//sta保存偶数个1的二进制数
}
int t;
for(scanf("%d",&t);t;--t)
{
int n,q;
scanf("%d%d",&n,&q);
memset(dp,0,sizeof(dp));
int now=0;
dp[0][0]=1;
while(q--)
{
memset(ans,0,sizeof(ans));
char s[2];
int x,y;
scanf("%s%d%d",s,&x,&y);
x--;
y--;
int tmp=(1<<x)|(1<<y);
if(s[0]=='+')
{
for(int i=0; i<cnt&&sta[i]<(1<<n); i++) //因为我们求的是图的完全匹配数,所以肯定是含有偶数个点的图,
{ //枚举偶数个点的状态
int cur=sta[i];
dp[now^1][cur]=dp[now][cur];
if((cur&tmp)==tmp) add(dp[now^1][cur],dp[now][cur^tmp]);
add(ans[num[cur]>>1],dp[now^1][cur]);
}
}
else
{
for(int i=0; i<cnt&&sta[i]<(1<<n); i++)
{
int cur=sta[i];
dp[now^1][cur]=dp[now][cur];
if((cur&tmp)==tmp) sub(dp[now^1][cur],dp[now][cur^tmp]);
add(ans[num[cur]>>1],dp[now^1][cur]);
}
}
now ^= 1;
for(int i=1; i<=n/2; ++i) printf("%d%c",ans[i],i==n/2?'\n':' ');
}
}
return 0;
}