【题意】:
t组样例,n个点(保证是偶数),m次操作,每次操作告诉你两个点,给这两个点加一条边或者减一条边,减边的话保证减边之前至少有一条边。
每次操作之后输出 整个图中 匹配大小为 j 的数量 。两条重边被认为是不一样的。
【题解】:
n <= 10,每个点有被占用和不被占用两种状态,所以一共有2^10种状态。
如果这次增加了 a b这两个点的一条边,那么就从 0 - 1023中找到,a和b在二进制相应位数下是0的状态,然后更新这个数字a和b相应位数下变成1的状态。
即用 num[a]表示1<<(a-1),dp[ i+num[a]+num[b] ]直接加上当前dp[ i ]的答案,并取模。
如果这次删除了a b这两个点的一条边,那么就从0 - 1023中找到,a和b在二进制相应位数下是1的状态,从a和b相应位数为0的状态来更新当前答案。
即dp[ i ] 减去 dp[ i -num[a] -num[b] ] 并且取模。
在加和减的操作的同时,对ans[ ]数组进行更新,ans[1]表示匹配大小为1的答案,以此类推。
【代码】:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod=1e9+7;
ll dp[1025];
ll num[15];//预处理,num[i]表示 1<<i
ll ans[15];
void add(int a,int b)
{
for(int i=0;i<1024;i++){
if((i&num[a])==0&&(i&num[b])==0){
int cnt=0;
for(int j=1;j<=i;j<<=1){
if((i&j)>0) cnt++;
}
if(cnt%2==0){
dp[i+num[a]+num[b]]=(dp[i+num[a]+num[b]]+dp[i])%mod;
ans[cnt/2+1]=(ans[cnt/2+1]+dp[i])%mod;
}
}
}
}
void sub(int a,int b)
{
for(int i=0;i<1024;i++){
if((i&num[a])>0&&(i&num[b])>0){
int cnt=0;
for(int j=1;j<=i;j<<=1){
if((i&j)>0) cnt++;
}
if(cnt%2==0){
dp[i]=(dp[i]-dp[i-num[a]-num[b]]+mod)%mod;
ans[cnt/2]=(ans[cnt/2]-dp[i-num[a]-num[b]]+mod)%mod;
}
}
}
}
int main(void)
{
int caz,n;
scanf("%d",&caz);
for(int i=0;i<11;i++){
num[i]=1<<i;
}
while(caz--){
int n,m;
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
dp[0]=1;
scanf("%d%d",&n,&m);
char ch[10];
int a,b;
for(int i=0;i<m;i++){
scanf("%s%d%d",ch,&a,&b);
if(ch[0]=='+'){
add(a-1,b-1);
}
else{
sub(a-1,b-1);
}
for(int i=1;i<=n/2;i++){
printf("%lld",ans[i]);
if(i==n/2) printf("\n");
else printf(" ");
}
}
}
return 0;
}