• USACO 2020 January Contest, Platinum Problem 1. Cave Paintings


    题目链接:http://usaco.org/index.php?page=viewproblem2&cpid=996

    提交评测:https://www.luogu.com.cn/problem/P6008

    题解:

      一开始,我想着从左往右进行统计方案数,然后发现转移方程太难写。从下往上进行统计就方便很多。首先要明白,不同的连通块的方案数乘积就是总的方案数。

      那么下面思考如何统计同一连通块中的方案数。假设现在统计到第i层:

      对于一段连续的空位,它有两种情况:

      1、它没有使得若干个块合并。那么这时这段区间的方案数是2

      2、使得若干个下层联通块合并,那么此时又分成对于每个下层连通块,有2种情况:(1)此连通块已经连通了第i层的块,那么此时对于这个连通块的方案数减一再乘到方案数中。(2)没有连通第i层块,直接此连通块的方案数乘到总方案数中。

      记得对于一段连续空位,把全部空位以及下层连通块合并,此操作可以并查集。

      时间复杂度:O(NMα(n*n))

    参考代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 1005;
    const int modu = 1e9 + 7;
    
    char s[maxn][maxn];
    int n, m;
    int fa[maxn*maxn];
    long long f[maxn*maxn];
    vector<int> st;
    
    int find(int x)
    {
        if (fa[x] == 0) return x;
        return fa[x] = find(fa[x]);
    }
    
    int number(int i, int j)
    {
        return i*m+j;
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; ++i)
            scanf("%s", s[i]);
        memset(fa, 0, sizeof(fa));
        memset(f, 0, sizeof(f));
        for (int i = n-2; i > 0; --i) {
            st.clear();
            for (int j = 0; j < m; ++j)
                if (s[i][j] == '.') {
                    int x = find(number(i, j));
                    if (s[i+1][j] == '.') {
                        int y = find(number(i+1, j));
                        st.push_back(y);
                    }
                    if (s[i][j-1] == '.') {
                        int y = find(number(i, j-1));
                        if (x != y) fa[y] = x;
                    }
                }
                else if (s[i][j] == '#') {
                    if (j > 0 && s[i][j-1] == '.') {
                        int x = find(number(i, j-1));
                        if (st.size() == 0) f[x] = 2;
                        else {
                            long long res = 1;
                            for (int k = 0; k < st.size(); ++k) {
                                int y = find(st[k]);
                                if (x != y) {
                                    if (y / m == i) res = res * ((f[y]-1+modu)%modu) % modu;
                                    else res = res*f[y] % modu;
                                    fa[y] = x;
                                }
                            }
                            f[x] = (res + 1) % modu;
                        }
                        st.clear();
                    }
                }
        }
        long long ans = 1;
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (s[i][j] == '.' && fa[number(i, j)] == 0) {
                    ans = ans*f[number(i, j)] % modu;
                }
        printf("%lld
    ", ans);
        return 0;
    }
  • 相关阅读:
    编程实现Windows系统自动登录
    IOS开发笔记
    所有的GUI Toolkit,类型之多真开眼界
    文件保护DEP
    Delphi 重启应用程序(创建Bat文件的Process)
    Linux的几个概念,常用命令学习
    Delphi内存操作API函数(备查,并一一学习)
    Servle中的会话管理
    Windows软件在Linux上的等价/替代/模仿软件列表 (抄一个)
    Go 的文件系统抽象 Afero
  • 原文地址:https://www.cnblogs.com/albert7xie/p/12487916.html
Copyright © 2020-2023  润新知