luoguP5074 Eat the Trees

https://www.luogu.org/problemnew/show/P5074

插头 $ dp $ 入门题

如果你还不会插头 $ dp $ 请右转 洛谷插头dp题解

虽然是入门题但还是逃不过分类讨论的魔爪

这里采用了括号序列的方法

$ left $ 表示左插头的状态,$ up $ 表示右插头的状态

情况 1:这个格子不能放线

只有在 $ left == 0 $ && $ up == 0 $ 的时候才能转移

情况 2:$ left == 0 $ && $ up == 0 $

因为每个格子一定要放线,所以要给这个格子加一个下插头和一个右插头

情况 3:$ left == 0 $ && $ up != 0 $

有一个上插头下来,没有左插头,为了让这个插头形成回路,我们给这个插头加一个下插头或者右插头

情况 4:$ left != 0 $ && $ up == 0 $

跟情况 3 类似,这里不再赘述

情况 5:$ left == 1 $ && $ up == 1 $

一个左插头和一个上插头都是左括号,要到右边去找到一个右括号来匹配,并且将右括号变成左括号

个人认为这是最难理解的一种情况,强烈建议大家自己去画个图

情况 6:$ left == 2 $ && $ up == 2 $

根情况 5 类似,只不过变成了到左边找左括号匹配

情况 7:$ left == 2 $ && $ up == 1 $

相当于左插头和上插头抵消了,将这两个插头直接去掉后,左插头本来有一个左括号和它匹配,上插头有一个右括号和它匹配,仍然满足括号序列的性质

情况 8:$ left == 1 $ && $ up == 2 $

形成了一个回路,直接连接即可

当然,如果这个回路是最后一个回路的话要统计答案,判断是否是最后一个回路只需判断这个格子是不是最后一个可以放线的点

最后这个题目有一个小坑,就是当整张地图都不能放线的时候还是算有一种情况的

下面是简(sang)单(xin)明(bing)了(kuang)的代码

#include <bits/stdc++.h>
#define Fast_cin ios::sync_with_stdio(false), cin.tie();
#define For(i, a, b) for(register int i = a; i <= b; i++)
#define Forr(i, a, b) for(register int i = a; i >= b; i--)
#define DEBUG(x) cerr << "DEBUG" << x << " >>> " << endl;
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

template <typename _T>
inline void read(_T &f) {
    f = 0; _T fu = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') fu = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
    f *= fu;
}

template <typename T>
void print(T x) {
    if(x < 0) putchar('-'), x = -x;
    if(x < 10) putchar(x + 48);
    else print(x / 10), putchar(x % 10 + 48);
}

template <typename T>
void print(T x, char t) {
    print(x); putchar(t);
}

const int mod = 5801, N = mod + 100;

ll f[2][N], ans;
int a[15][15], tot[2], v[2][N], nxt[N], head[N], bin[15];
int T, n, m, now, end1, end2;

void ins(int zt, ll val) {
    int u = zt % mod;
    for(register int i = head[u]; i; i = nxt[i])
        if(v[now][i] == zt) {
            f[now][i] += val;
            return;
        }
    nxt[++tot[now]] = head[u]; head[u] = tot[now];
    f[now][tot[now]] = val; v[now][tot[now]] = zt;
}

void sol() {
    tot[now] = 1; f[now][1] = 1; v[now][1] = 0;
    for(register int i = 1; i <= n; i++) {
        for(register int j = 1; j <= tot[now]; j++) v[now][j] <<= 2;
        for(register int j = 1; j <= m; j++) {
            now ^= 1; memset(head, 0, sizeof(head)); tot[now] = 0;
            for(register int k = 1; k <= tot[now ^ 1]; k++) {
                int zt = v[now ^ 1][k]; ll val = f[now ^ 1][k];
                int t1 = (zt >> ((j << 1) - 2)) & 3, t2 = (zt >> (j << 1)) & 3;
                if(a[i][j] == 0) {
                    if(t1 == 0 && t2 == 0) ins(zt, val);
                } else if(t1 == 0 && t2 == 0) {
                    if(a[i][j + 1] && a[i + 1][j]) ins(zt ^ bin[j - 1] ^ (bin[j] << 1), val);
                } else if(t1 == 0 && t2 != 0) {
                    if(a[i][j + 1]) ins(zt, val);
                    if(a[i + 1][j]) ins(zt ^ (bin[j] * t2) ^ (bin[j - 1] * t2), val);
                } else if(t1 != 0 && t2 == 0) {
                    if(a[i + 1][j]) ins(zt, val);
                    if(a[i][j + 1]) ins(zt ^ (bin[j] * t1) ^ (bin[j - 1] * t1), val);
                } else if(t1 == 1 && t2 == 1) {
                    int nowv = 1;
                    for(register int t = j + 1; t < m; t++) {
                        int t3 = (zt >> (t << 1)) & 3;
                        if(t3 == 1) nowv++;
                        if(t3 == 2) nowv--;
                        if(!nowv) { ins(zt ^ bin[j - 1] ^ bin[j] ^ (bin[t] * 3), val); break; }
                    }
                } else if(t1 == 2 && t2 == 2) {
                    int nowv = 1;
                    for(register int t = j - 2; t > 0; t--) {
                        int t3 = (zt >> (t << 1)) & 3;
                        if(t3 == 1) nowv--;
                        if(t3 == 2) nowv++;
                        if(!nowv) { ins(zt ^ (bin[j - 1] << 1) ^ (bin[j] << 1) ^ (bin[t] * 3), val); break; }
                    }
                } else if(t1 == 2 && t2 == 1) {
                    ins(zt ^ (bin[j - 1] << 1) ^ bin[j], val);
                } else if(i == end1 && j == end2) {
                    ans += val;
                } else ins(zt ^ bin[j - 1] ^ (bin[j] << 1), val);
            }
        }
    }
}

int main() {
    read(T); bin[0] = 1;
    for(register int i = 1; i <= 11; i++) bin[i] = bin[i - 1] << 2;
    for(register int Case = 1; Case <= T; Case++) {
        memset(a, 0, sizeof(a));
        read(n); read(m); ans = 0; end1 = end2 = 0;
        for(register int i = 1; i <= n; i++) {
            for(register int j = 1; j <= m; j++) {
                read(a[i][j]);
                if(a[i][j]) end1 = i, end2 = j;
            }
        }
        if(!end1) { print(1, '\n'); continue; }
        sol(); print(ans, '\n');
    }
    return 0;
}
posted @ 2018-12-08 23:38 LJC00118 阅读(...) 评论(...) 编辑 收藏