Particle

競技プログラミングについての雑記

AOJ 2231 : Cruel Bingo

解法

包除原理+DP

包除原理に気づかずbitDPしようとしたら複雑すぎて無理だった(解説スライド見ても解けるとしか書いてなかった)
あと、解説スライド見ると包除原理だけで良いらしい(ちょっと読んでもよく分からなかった)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long ll;
#define N 40
#define M 8
ll ux[N], uy[N], g[N][N], dp[N][N][4], dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
ll mod = 10007;
int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	int s = n&1, t = n/2, t2 = (n-1)/2;
	for(int i = 0; i < m; i++){
		int x, y;
		scanf("%d%d",&x,&y);
		ux[x] |= 1<<i;
		uy[y] |= 1<<i;
	}
	ll res = 0;
	for(int i = 0; i < 1<<m; i++){
		int bt = __builtin_popcount(i);
		int dg = 0;
		bool ok = true;
		for(int j = 0; j < n; j++){
			if(__builtin_popcount(ux[j]&i)>1 || __builtin_popcount(uy[j]&i)>1){
				ok = false;
			}
			for(int k = 0; k < n; k++){
				g[j][k] = (ux[j]|uy[k])&i;
				if(ux[j]&uy[k]&i){
					if(j==k) dg |= 1;
					if(j==n-k-1) dg |= 2;
				}
			}
		}
		if(!ok) continue;
		memset(dp, 0, sizeof(dp));
		dp[0][bt][dg] = 1;
		if(s&&!g[t][t]) dp[0][bt+1][3] = 1;
		for(int j = 1; j <= t; j++){
			int left = t-j;
			int len = 2*j+s;
			int fr[4] = {}, fr2[4] = {};
			for(int k = 0, a = left, b = left; k < 4; k++){
				for(int l = 0; l < len-1; l++, a+=dx[k], b+=dy[k]){
					if(!g[a][b]){
						if(l==0) fr2[k] = 1;
						else fr[k]++;
					}
				}
			}
			for(int k = bt; k <= n; k++){
				int ms = k-bt;
				int fr3[4], fr4[2];
				for(int l = 0; l < 4; l++) fr3[l] = max(0, fr[l]-ms);
				fr4[0] = max(0, fr3[0]*(fr3[2]-1));
				fr4[1] = max(0, fr3[1]*(fr3[3]-1));
				for(int l = 0; l < 4; l++){
					int dd = dp[j-1][k][l];
					(dp[j][k][l]+=dd)%=mod;
					(dp[j][k+1][l]+=(fr3[0]+fr3[1]+fr3[2]+fr3[3])*dd)%=mod;
					(dp[j][k+1][l|1]+=(fr2[0]+fr2[2])*dd)%=mod;
					(dp[j][k+1][l|2]+=(fr2[1]+fr2[3])*dd)%=mod;
					(dp[j][k+2][l]+=(fr3[0]*fr3[1]+fr4[0]+fr3[0]*fr3[3]+fr3[1]*fr3[2]+fr4[1]+fr3[2]*fr3[3])*dd)%=mod;
					(dp[j][k+2][l|1]+=(fr2[0]*(fr3[1]+fr3[2])+fr2[2]*(fr3[3]+fr3[0])+fr2[0]*fr2[2])*dd)%=mod;
					(dp[j][k+2][l|2]+=(fr2[1]*(fr3[2]+fr3[3])+fr2[3]*(fr3[0]+fr3[1])+fr2[1]*fr2[3])*dd)%=mod;
					(dp[j][k+3][l]+=(fr4[0]*(fr3[1]+fr3[3])+fr4[1]*(fr3[0]+fr3[2]))*dd)%=mod;
					(dp[j][k+3][l|1]+=(fr2[0]*(fr3[1]*fr3[2])+fr2[2]*(fr3[3]*fr3[0]))*dd)%=mod;
					(dp[j][k+3][l|2]+=(fr2[1]*(fr3[2]*fr3[3])+fr2[3]*(fr3[0]*fr3[1]))*dd)%=mod;
					(dp[j][k+4][l]+=(fr4[0]*fr4[1])*dd)%=mod;
				}
			}
		}
		(res+=dp[t][n][3]*(bt&1?-1:1))%=mod;
	}
	printf("%ld\n",(res+mod)%mod);

	return 0;
}