bzoj 3195: [Jxoi2012]奇怪的道路

链接:3195: [Jxoi2012]奇怪的道路

  • 大意:给定\(n\)\(m\)\(k\),求出满足下列两个要求的图的个数,允许重边己环和不联通:
  • 每条边\(1<=|u-v|<=K\)且每个点连边偶数。$n,m\leq 30 \(,\)k\leq 8$
  • 有个朴素的\(Dp\)就是状压\(k\),设\(f_{i,j,s}\)表示前\(i\)个点,\(j\)条边,最后\(k\)个点的奇偶性为\(s\)的方案数。
  • 然后枚举\(i\)和谁连边向\(j+1\)转移,每个\(s\)再向\(i+1\)转移。
  • 写完后发现这个做法有\(bug\),因为同一个状态因为\(i\)向外连边的顺序不同而被重复记数了。
  • 其实很好解决,\(i\)连向相同一个点的边就很像背包问题中的一件物品,每一件物品都是不限量的(只是最终要求了奇偶)。
  • 想一想,我们的完全背包统计方案时并没有多开一维,但绝对不会重复计数的。
  • 原因很简单,在做背包问题时,各个物品的选取存在严格顺序,不存在选了几个物品\(a\),又选了一点别的,再去选了几个物品\(a\)的情况。
  • 所以这里应当严格区分出各个物品,在每一个物品内部做完全背包。
  • 代码很短
#include<bits/stdc++.h>
#define R register int
using namespace std;
const int mod=1000000007;
int n,m,k,now,lm,f[2][35][600];
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
int main(){
	freopen("s.in","r",stdin);
	cin>>n>>m>>k,lm=(1<<(k+1)),f[now][0][0]=1;
	for(R i=1;i<=n;++i){
		now^=1,memset(f[now],0,sizeof(f[now]));
		for(R j=0;j<=m;++j)
			for(R S=0;S<(1<<k);++S)
				f[now][j][S<<1]=f[now^1][j][S];
		for(R p=1;p<=min(i-1,k);++p)
			for(R j=0;j<=m;++j)
				for(R S=0;S<lm;++S)
					add(f[now][j+1][S^1^(1<<p)],f[now][j][S]);
	}
	cout<<f[now][m][0];
    return 0;
}

posted @ 2018-10-21 21:38  Tyher  阅读(116)  评论(0编辑  收藏  举报