HDU-6407 Pop the Balloons(DP)

Pop the Balloons

Time Limit: 7000/7000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 87    Accepted Submission(s): 27


 

Problem Description

Recently, an interesting balloon-popping game can be commonly found near the streets. The rule of this game is quite simple: some balloons are tied to cells of an n×m lattice, and you are allowed to throw k darts to prick the balloons. The more balloons you pierce, the more incredible prize you will get.

Probably because too many people have got bored with this childish game, a new variation of the game has appeared. In this variation, the balloons are replaced with more powerful ones: when a balloon explodes, strong gusts travel in four directions, blowing away all remaining balloons in the same row and column. In order to reduce the difficulty, not all cells are filled with a balloon.

For example, if you prick the yellow balloon in the following figure, then the red balloons will be blown away, with only the blue balloon remaining.



Now, you are given k darts. Since you are a good dart player that you can precisely throw it to any position you want, it is easy for you to use these k darts to clear all balloons (either by directly pricking, or by blowing away by other exploded balloons). Now you begin to consider: for every 1≤x≤k, how many different ways are there to clear all balloons with exactly x darts? Two ways are considered different if and only if there exists i, such that the positions of i-th pricked balloons differ. Note that you cannot throw the dart to an empty cell.

Input

The first line of input is a single integer T (1≤T≤100), denoting the number of test cases.

Each test case begins with a line of three integers m,n (1≤m≤12,1≤n≤20), denoting the size of the balloon lattice, and k (1≤k≤20), denoting the number of darts. The next m lines, each containing n characters, describe the balloon lattice. Each character is either 'Q', which denotes a balloon, or '.', which denotes an empty cell.

Output

For each test case, display k lines. The i-th line is a single integer denoting the number of different ways of clearing all balloons with exactly i darts.

Sample Input

4 2 2 2 QQ .Q 2 2 2 QQ .. 3 3 3 .Q. QQQ .Q. 1 3 1 Q.Q

Sample Output

1 2 2 0 1 8 0 2

Source

2018 Multi-University Training Contest 8

题意:m*n的矩阵(注意:m*n!!!!!剧毒),有些格子有气球,如果扎破一个气球,则同行同列的气球都会破掉,现在给出一个K,问对于所有x有1<=x<=K,刚好扎破x个气球后所有气球都破掉的方案数(顺序不同视为不同方案)。

题解:设dp[i][S]为,当前为第i行,S表示每一列的气球状态,用3进制表示,0表示该列没有气球,1表示该列有气球,2表示该列已经扎破了一个气球。很容易得到转移方程:dp[i][ss] += dp[i - 1][s],其中ss表示扎破第i行第j列的气球从s转移过来的状态。

这道题有个坑,因为不同顺序也是不同方案,因此最后还要乘上x!,然后就会爆longlong。。。因此需要用int128。。。

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=a-1;i>=b;--i)
#define fuck(x) cout<<'['<<#x<<' '<<(x)<<']'
#define eps 1e-12
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> VI;
typedef pair<int, int> PII;
const int mod = 998244353 ;

const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MX = 540000;
ll dp[21][MX], ans[21];
char mp[21][21];
int a[21][21];
int f[4][21], g[MX][13];
void pre_solve() {
    f[1][0] = 1; rep(i, 1, 13) f[1][i] = f[1][i - 1] * 3;
    f[2][0] = 2; rep(i, 1, 13) f[2][i] = f[2][i - 1] * 3;
    f[3][0] = 1; rep(i, 1, 21) f[3][i] = f[3][i - 1] * i;
    int mx = 1; rep(i, 0, 12) mx *= 3;
    rep(i, 0, mx) {
        int tmp = i;
        rep(j, 0, 12) g[i][j] = tmp % 3, tmp /= 3;
    }
}
int main() {
#ifdef local
    freopen("in.txt", "r", stdin);
#endif // local
    pre_solve();
    int T; cin >> T;
    while(T--) {
        int n, m, k; cin >> n >> m >> k;
        rep(i, 1, n + 1) scanf("%s", mp[i]);
        rep(i, 1, n + 1) rep(j, 0, m) a[j + 1][i - 1] = mp[i][j] == 'Q' ? 1 : 0;
        swap(n, m);
        rep(i, 1, k + 1) ans[i] = 0;
        int mx = 1; rep(j, 0, m) mx *= 3;
        rep(i, 1, n + 1) rep(s, 0, mx) dp[i][s] = 0;
        dp[0][0] = 1;
        int ss;
        rep(i, 1, n + 1) rep(s, 0, mx) if(dp[i - 1][s]) {
            rep(j, 0, m) if(a[i][j] && g[s][j] <= 1) {
                dp[i][s - f[g[s][j]][j] + f[2][j]] += dp[i - 1][s];
            }
            ss = s;
            rep(j, 0, m) if(a[i][j] && g[s][j] == 0) ss += f[1][j];
            dp[i][ss] += dp[i - 1][s];
        }
        rep(s, 0, mx) if(dp[n][s]) {
            bool flag = 1; int cnt = 0;
            rep(j, 0, m) {
                if(g[s][j] == 1) {flag = 0; break;}
                if(g[s][j] == 2) cnt++;
            }
            if(flag) ans[cnt] += dp[n][s];
        }
        ll mod = 1000000000000ll;
        rep(i, 1, k + 1) {
            __int128 tmp = (__int128)f[3][i] * ans[i];
            if(tmp > mod) printf("%lld%012lld\n", (ll)(tmp / mod), (ll)(tmp % mod));
            else printf("%lld\n", (ll)tmp);
        }
    }

#ifdef local
    cout << "time cost:" << clock() << "ms" << endl;
#endif // local
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值