题目链接:http://community.topcoder.com/stat?c=problem_statement&pm=14070&rd=16551
大致题意:
描述太麻烦。摘取关键题意:
When pushing the trees, Limak always follows a few rules:
- He only pushes trees in two directions: either southwards or eastwards.
- He will never push a tree in a way that would cause it to fall out of the forest. For example, he will never push a tree in the last column eastwards.
- He will never push a tree in a way that would produce two fallen trees lying on the same cell.
- Is there a fallen tree in the current cell? If yes, there is no room here to do anything, so I'll just move to the next cell.
- Can I push the tree in the direction that is given by the letter written on the tree? If yes, I'll do so and move to the next cell.
- Can I push the tree in the other direction? If yes, I'll do so and move to the next cell.
- I'll move to the next cell without pushing the tree.
You are given the ints W and H . There are 2^( W * H ) different forests with these dimensions. (Different forests have different assignments of letters S and E to the trees.) For each of these forests, compute the number of trees Limak would topple. Return the sum of those 2^( W * H ) numbers, modulo MOD .
思路:
第一行状压01表示S和E,然后某些树会倒在第二行上,把倒下的位置状压转移到第二行。
每一行产生的贡献值之和这行SE和上一行倒下的树产生的影响。
所以可以dp[premask][curmask].first 表示上一行产生的影响是premask,这行SR分布是curmask时,可以推倒多少颗树木
dp[premask][curmask].second 表示推倒这些树木后对下一行产生的影响
然后统计总和纪录到此行为止,cnt[mask]一共贡献了多少答案和一共有多少种方式产生这个mask,方便接下来用乘法原理递推每一行
复杂度是O( (2^W) * (2^W) * H)
// Paste me into the FileEdit configuration dialog
// Paste me into the FileEdit configuration dialog
#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdio>
#include <ctime>
#include <bitset>
#include <algorithm>
#define SZ(x) ((int)(x).size())
#define ALL(v) (v).begin(), (v).end()
#define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i)
#define reveach(i, v) for (__typeof((v).rbegin()) i = (v).rbegin(); i != (v).rend(); ++ i)
#define REP(i,n) for ( int i=1; i<=int(n); i++ )
#define rep(i,n) for ( int i=0; i< int(n); i++ )
using namespace std;
typedef long long ll;
#define X first
#define Y second
#define PB push_back
#define MP make_pair
typedef pair<ll,ll> pii;
const int N = 8;
pii dp[1<<N][1<<N];
bool ok[N];
pii cnt[1<<N][2]; // X - > kind, Y -> sum
ll last[1<<N];
class BearDestroysDiv2 {
public:
int sumUp( int W, int H, int MOD ) {
int n = W, m = H;
rep(i, 1 << n) {
ll la = 0;
rep(j, 1 << n) {
memset(ok, 0, sizeof(ok));
int state = 0;
ll c = 0;
rep(p, n) {
if( i & ( 1 << p) ) continue ;
if( (j & (1 << p)) ) {
if( ( (p == 0) || (p > 0 && ok[p-1] == 0) ) && p != n-1 && (i&(1<<(p+1)))==0){
c ++;
ok[p] = 1;
} else if( ((p == 0)||(p > 0 && ok[p-1] == 0))) {
c ++ ;
state |= ( 1 << p );
}
} else {
if( ( (p == 0) || (p > 0 && ok[p-1] == 0) ) ) {
c ++;
state |= ( 1 << p );
}
}
}
dp[i][j] = pii(c % MOD, state);
memset( ok ,0 ,sizeof(ok));
rep(p, n) {
if( i & ( 1 << p) ) continue ;
if( (j & (1 << p)) ) {
if( ( (p == 0) || (p > 0 && ok[p-1] == 0) )&& p != n-1 && (i&(1<<(p+1)))==0 ) {
la ++ ;
ok[p] = 1;
}
} else {
if( ( (p == 0) || (p > 0 && ok[p-1] == 0) )&& p != n-1 && (i&(1<<(p+1)))==0 ) {
la ++;
ok[p] = 1;
}
}
}
}
last[i] = la % MOD;
}
memset(cnt, -1 , sizeof(cnt));
cnt[0][1].X = 1;
cnt[0][1].Y = 0;
for(int h = 1; h < m; h ++) {
int cur = h & 1;
rep(mask, 1 << n) cnt[mask][cur^1] = pii(-1, -1);
rep(pre, 1 << n) {
if( cnt[pre][cur] == pii(-1,-1) ) continue ;
rep(mask, 1 << n) {
pii &ans = cnt[dp[pre][mask].Y][cur^1];
if( ans == pii(-1, -1) ) ans = pii(0, 0);
ans.X = (ans.X + cnt[pre][cur].X) % MOD;
ans.Y = (ans.Y + cnt[pre][cur].Y + cnt[pre][cur].X * dp[pre][mask].X) % MOD ;
}
}
}
ll ans = 0;
rep(mask, 1 << n) {
if( cnt[mask][m&1] == pii(-1,-1) ) continue;
ans = ( ans + cnt[mask][m & 1].X * last[mask] + cnt[mask][m & 1].Y * (1 << n) ) % MOD;
}
return ans ;
}
};