1. 程式人生 > 實用技巧 >聯賽模擬測試25 A. Adore 狀壓DP

聯賽模擬測試25 A. Adore 狀壓DP

題目描述


分析

看到 \(k \leq 10\) 和有關奇偶的問題就應該聯想到狀壓 \(DP\)
我們用狀態 \(0\) 表示表示到當前的點有偶數條路徑,用 \(1\) 表示到當前的點有奇數條路徑
對於每一層的點,我們儲存正向和反向時該點能到達的點的集合
然後分兩種情況轉移即可
時間複雜度 \(O(mk \times 2^k)\)

程式碼

#include<cstdio>
#include<vector>
#include<cstring>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e4+5,maxk=12;
const int mod=998244353;
int m,k,f[maxn][1<<maxk],a[maxk],b[maxk],ans,mmax;
bool vis[maxk];
int main(){
	m=read(),k=read();
	rg int aa,now=0;
	for(rg int i=1;i<=k;i++){
		aa=read();
		now|=(aa<<(i-1));
	}
	mmax=(1<<k)-1;
	f[1][now]=1;
	for(rg int i=2;i<m-1;i++){
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		for(rg int j=1;j<=k;j++){
			for(rg int o=1;o<=k;o++){
				aa=read();
				a[j]|=(aa<<(o-1));
				b[o]|=(aa<<(j-1));
			}
		}
		for(rg int j=0;j<=mmax;j++){
			if(f[i-1][j]){
				rg int now1=0,now2=0;
				for(rg int o=1;o<=k;o++){
					if(j&(1<<(o-1))){
						now1^=a[o];
						now2^=b[o];
					}
				}
				f[i][now1]+=f[i-1][j];
				if(f[i][now1]>=mod) f[i][now1]-=mod;
				f[i][now2]+=f[i-1][j];
				if(f[i][now2]>=mod) f[i][now2]-=mod;
			}
		}
	}
	for(rg int i=1;i<=k;i++){
		vis[i]=read();
	}
	for(rg int i=0;i<=mmax;i++){
		if(f[m-2][i]){
			rg int cnt=0;
			for(rg int j=1;j<=k;j++){
				if(vis[j] && i&(1<<(j-1))) cnt++;
			}
			if(cnt&1) continue;
			ans+=f[m-2][i];
			if(ans>=mod) ans-=mod;
		}
	}
	printf("%d\n",ans);
	return 0;
}