题目链接:哆啦A梦传送门
题意:给n个点,m个操作,每次为 + u v或者 - u v,+ 表示点u与点v之间连一条边,反之 - 亦然。每次操作完成后,输出n/2个数,分别表示当前选出1条无公共端点的边,2条无公共端点的边,3条.....n/2条无公共端点边时分别有多少种不同的选择。
题解:参考博客:https://blog.csdn.net/paradise_tht/article/details/81385383
官方解释:
详情见代码:
还需注意一点的是,取模是尽量不要用取模函数,这里我就是因为这而被T。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=1300;
LL mod=1e9+7;
LL dp[maxn][12]; ///dp[i][j]表示在状态i下(也就是有多少个点),j条无公共端点的边有多少种不同的选择
int cnt[maxn];
char op[10];
int main()
{
for(int i=1;i<maxn;i++) ///初始化,预处理cnt[i]表示i的二进制有多少个1
cnt[i]=cnt[i>>1]+(i&1);
int ncase;
scanf("%d",&ncase);
int n,m;
while(ncase--)
{
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
int u,v,uv,S=(1<<n)-1; ///S表示满点状态
for(int i=0;i<=S;i++)
dp[i][0]=1;
while(m--)
{
scanf("%s%d%d",op,&u,&v);
u--;v--;
uv=(1<<u)|(1<<v); ///将u,v点转化成二进制uv,表示含有u,v点的状态
for(int i=S;i>=0;i--) ///我们从大(满点)到小dp,
{
if((i&uv)!=uv) continue; ///此状态不包含uv,故跳过
for(int j=1;j<=cnt[i];j++)
{
if(op[0]=='+') dp[i][j]+=dp[i^uv][j-1];
else dp[i][j]+=(mod-dp[i^uv][j-1]);
if(dp[i][j]>=mod) ///取模,不能用下面直接模,会T
dp[i][j]-=mod;
///因为下面的方法调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算,
///而上面的方法则仅仅是几句相关的汇编,代码更简洁、效率更高。
// dp[i][j]%=mod;
// printf("s=%d,j=%d,uv=%d,%d %d\n",i,j,i^uv,dp[i][j],dp[i^uv][j-1]);
}
}
for(int i=1;i<n/2;i++)
printf("%lld ",dp[S][i]);
printf("%lld\n",dp[S][n/2]);
}
}
return 0;
}