并查集的计数方法

https://www.luogu.com.cn/problem/P6008

十分玄学

刚开始打算建森林树形dp,结果不会建立树,看了题解发现合并集合时候乘起来就好,没必要非得建树,想法和树形dp一样

dp1表示画水,dp0表示不用水,由于高的点画了水,连通块的点都会有水,所以高处放水只有一种方案。

由此可以从下往上推,集合相互合并就把答案乘起来,具体看代码把,不好想,很好写。

#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6+111;
ll mod = 1e9+7;
int n,m;
char map[1010][1010];
int id[1010][1010];
int par[maxn];
ll ans[maxn];
int find(int x){
    if(par[x] == -1) return x;
    return par[x] = find(par[x]);
}
int unin(int x,int y){
    int xx = find(x);
    int yy = find(y);
    if(xx == yy ){
        return 0;
    }
    par[yy] = xx;
    ans[xx] = (ans[yy]*ans[xx])%mod;
    return 0;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%s",map[i]);
    }
    int len = 0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(map[i][j] == '.'){
                id[i][j] = ++len;
            }
        }
    }

    for(int i=0;i<=len+100;i++){
        par[i] = -1;
        ans[i] = 1;
    }

    for(int i = n-1;i>=0;i--){
        for(int j=0;j<m;j++){
            if(i != n-1){
                if(map[i][j] == '.' && map[i+1][j] == '.'){
                    unin(id[i][j],id[i+1][j]);
                }
            }

            if(j != 0){
                if(map[i][j] == '.' && map[i][j-1] == '.'){
                    unin(id[i][j],id[i][j-1]);
                }
            }
        }

        for(int j=0;j<m;j++){
            if(map[i][j] == '.'){
                int x = id[i][j];
                if(par[x] == -1){
                    ans[x] = (ans[x]+1)%mod;
                }
            }
        }
    }
    ll chal = 1;
    for(int i=1;i<=len;i++){
        if(par[i] == -1){
            chal = (chal * ans[i])%mod;
        }
    }
    printf("%lld
",chal);
    return 0;
}

  

寻找真正的热爱
原文地址:https://www.cnblogs.com/lesning/p/13736985.html