• [USACO 2020.1 Platinum][LOJ3246]Cave Paintings(bfs+树形dp)


    题面

    https://loj.ac/problem/3246

    题解

    注意到如果两个同在第i行的空格子,如果它们能够只通过原图的i~n行这部分实现连通,那么它们最终的状态一定是相同的。

    考虑把所有这样的格子合并。也就是,我们将每一行的空格子分别染色,某一行中两个格子的颜色相同当且仅当这两个格子符合上述条件。石头格子没有颜色。

    最下面一行的染色方案显然。而只需要经过bfs,就可以用第i+1行的染色方案推出第i行的染色方案。

    染色结束后,我们可以把每一个二元组((i,j))(表示所有第i行的第j种颜色的格子)视为一个点,然后连边,((i_1,j_1))((i_2,j_2))有一条单向边当且仅当(i_2=i_1+1),并且({exists}x{in}(1,n)),s.t.第(i_1)行第x个格子为空、颜色是(j_1),且第(i_2)行第x个格子为空、颜色是(j_2)

    容易发现这样的话每个点只会有一条入边。连完边后,就形成了一棵森林,就可以使用下面的递推式计算点u的答案:

    [dp(u)=1+{prodlimits_{v{in}son[u]}}dp(v) ]

    • 其中1是u点被灌水的情况,那么其子树必须被灌水。否则,u的各子节点独立,可以使用乘法原理合并。

    最终答案即为森林中所有的根节点的dp值之积。

    时间复杂度为O(n)。

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define N 1000
    #define mod 1000000007
    #define rg register
    #define ll long long
    
    inline int read(){
    	int s = 0,ww = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
    	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
    	return s * ww;
    }
    
    int n,m;
    
    inline int id(int i,int j){ //将“第i行的第j种颜色”这个二元组编号压入一维 
    	return (i - 1) * m + j;
    }
    
    char s[N+5][N+5];
    bool vis[N*N+5]; 
    int clr[N+5][N+5],cn[N+5],head[N*N+5],fa[N*N+5];//clr表示颜色(连通性),cn表示每一行的颜色个数 
    int cnt;
    
    struct edge{
    	int next,des;
    }e[N*N+5];
    
    inline void addedge(int a,int b){
    	cnt++;
    	e[cnt].des = b;
    	e[cnt].next = head[a];
    	head[a] = cnt;
    }
    
    inline ll dp(int u){ //每一个u代表一个(i,j)的二元组,表示第i行的颜色j 
    	ll cur = 1;
    	vis[u] = 1;
    	for(rg int i = head[u];i;i = e[i].next){
    		int v = e[i].des;
    		cur = cur * dp(v) % mod;
    	}
    	return (cur + 1) % mod;
    }
    
    struct Graph{ //用来转移clr的辅助图 
    	int cnt;
    	vector<int>link[3*N+5];
    	int f[3*N+5];
    	queue<int>q;
    	
    	inline void reset(){
    		cnt = 0;
    		for(rg int i = 1;i <= 3 * m;i++)link[i].resize(0);
    		memset(f,0,sizeof(f));
    	}
    	
    	inline void addedge(int a,int b){
    		link[a].push_back(b);
    		link[b].push_back(a);
    	}
    	
    	inline bool bfs(int s){
    		bool flag = 0;
    		q.push(s);
    		while(!q.empty()){
    			int u = q.front();
    			q.pop();
    			for(rg int i = 0;i < link[u].size();i++){
    				int v = link[u][i];
    				if(!f[v]){
    					if(v <= m)flag = 1;
    					f[v] = f[u];
    					q.push(v);
    				}
    			}
    		}
    		return flag;
    	}
    	
    	inline void calc(int i){
    		reset();
    		for(rg int j = 1;j < m;j++){
    			if(s[i][j] == '.' && s[i][j+1] == '.')addedge(id(1,j),id(1,j+1));
    			if(s[i+1][j] == '.' && s[i+1][j+1] == '.')addedge(id(2,j),id(2,j+1));
    			if(s[i][j] == '.' && s[i+1][j] == '.')addedge(id(1,j),id(2,j));
    			if(s[i+1][j] == '.')addedge(id(2,j),id(3,clr[i+1][j]));
    		} //id(1,…)和(2,…)代表原图的第i,i+1行;如果原图第i+1行第j格颜色为c,那么辅助图中id(2,j)就要向id(3,c)连边
    		for(rg int j = 1;j <= cn[i+1];j++){
    			if(f[id(3,j)])continue;
    			f[id(3,j)] = ++cn[i];
    			if(!bfs(id(3,j)))cn[i]--; //第i+1行颜色为j的这一块不与第i行的任何块连通 
    		}
    		for(rg int j = 1;j < m;j++)if(f[id(1,j)])clr[i][j] = f[id(1,j)];
    	}
    }G;
    
    int main(){
    	freopen("cave.in","r",stdin);
    	freopen("cave.out","w",stdout);
    	n = read(),m = read();
    	bool flag = 1;
    	for(rg int i = 1;i <= n;i++)scanf("%s",s[i] + 1);
    	for(rg int i = n - 1;i >= 2;i--){
    		G.calc(i);
    		for(rg int j = 2;j < m;j++)if(s[i][j] == '.' && s[i+1][j] == '.')
    			fa[id(i+1,clr[i+1][j])] = id(i,clr[i][j]);
    		for(rg int j = 1;j <= cn[i+1];j++)
    			if(fa[id(i+1,j)])addedge(fa[id(i+1,j)],id(i+1,j));
    		for(rg int j = 2;j < m;j++)if(s[i][j] == '.' && !clr[i][j]){
    			if(s[i][j-1] == '.')clr[i][j] = clr[i][j-1];
    			else clr[i][j] = ++cn[i];
    		}
    	}
    	ll ans = 1;
    	for(rg int i = 2;i < n;i++)
    		for(rg int j = 2;j < m;j++){
    			if(s[i][j] == '.' && !vis[id(i,clr[i][j])])ans = ans * dp(id(i,clr[i][j])) % mod;
    		}
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    Java蛇形数组的简单实现代码
    Android Studio生成javadoc出错的解决办法
    AngularJS指令嵌套时link函数执行顺序的问题
    [转]如果我有jQuery背景,我应该如何切换到AngularJS的思维模式?
    扩展ViewFlow避免和ViewPager滑动冲突,同时支持无限循环,并完美和CircleFlowIndicator结合
    人机交互的新方向:智能聊天机器人
    利用python自动清除Android工程中的多余资源
    巧用svn create patch(打补丁)方案解决定制版需求
    【Android开发坑系列】之经常被忽略的背景图片内存泄露
    【Android开发坑系列】之try-catch
  • 原文地址:https://www.cnblogs.com/xh092113/p/12273848.html
Copyright © 2020-2023  润新知