300iq Contest 3 C. Cells Blocking

题意:给一个图,’.‘代表空地,’*‘代表障碍,每次能往右或者往下走。问有多少点对满足,把这两个空地堵上以后,使得无法从左上角(1,1)走到右下角(n,m)

题解:预处理出从(1,1)出发,从(n,m)倒退,所能到达的点,将其他的不能达到的空地删除(删除前先统计总空地个数)。如果不能从(1,1)出发到达(n,m),直接得出答案。

将这些点按照他们所处的斜对角线分组。

显然,若一条对角线上只有一个空地,那这个空点和任意一个空地组合都可以,堵上整条路。直接统计这部分点的个数。

若一条对角线上多个空地。那么如果要选其中一些空地堵住。只能选其中最边缘的两个空地。

首先,我们忽略掉对角线上只有一个空地的情况,因为这部分的点对已经统计完了。

对于剩下的任意两段对角线,他们最左边的两个空地,和最右边的两个空地一定是可以连通的,因为只能往右和往下走。比如如果不能从(3,1)到达(5,2),那(3,1)要成为空地一定要能到达(4,3)(或者右边别的空地)。那么(5,2)要成为空地,一定要从(2,2)到达。而如果(2,2)能够到达(5,2),(3,1)也一定能(它们的路径一定有交点)。

所以如果一条斜线三个或更多的空地,也只能选择最左或者最右的空地堵上其中一个或两个。堵上中间的点,无论后面怎么堵,至少能从两端其中一个空地到达终点。

1,1 1,2 1,3 1,4 1,5
2,1 2,2 2,3 2,4 2,5
3,1 3,2 3,3 3,4 3,5
4,1 4,2 4,3 4,4 4,5
5,1 5,2 5,3 5,4 5,5

假设堵上最左边的空地,那么我们现在要枚举,堵上右下方斜线的哪些点能使得路径完全被堵上。

我们从这条斜线剩余的次左边和最右边的两个空地出发。对于次左边的空地,如果能往下走,就尽量往下走。否则往右。最右边的空地,如果能往右走就往游走。如果在往右下走的过程中有汇合。说明我们堵上当前斜线上的这块空地也能堵死整条路。

代码是300iq的代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
char c[3005][3005];
bool dp[3005][3005];
bool dp2[3005][3005];
vector<int>v[6005];
ll cnt=0,ans=0;
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i=1; i<=n ;i++){
		for(int j=1; j<=m ;j++){
			cin >> c[i][j];
			dp[i][j]|=dp[i-1][j];
			dp[i][j]|=dp[i][j-1];
			dp[i][j]|=(i==1 && j==1);
			dp[i][j]&=(c[i][j]=='.');
			cnt+=(c[i][j]=='.');
		}
	}
	for(int i=n; i>=1 ;i--){
		for(int j=m; j>=1 ;j--){
			dp2[i][j]|=dp2[i+1][j];
			dp2[i][j]|=dp2[i][j+1];
			dp2[i][j]|=(i==n && j==m);
			dp2[i][j]&=(c[i][j]=='.');
		}
	}
	for(int i=1; i<=n ;i++){
		for(int j=1; j<=m ;j++){
			if(!dp[i][j] || !dp2[i][j]) c[i][j]='*';
			else v[i+j].push_back(i);
		}
	}
	if(!dp[n][m]) return cout << cnt*(cnt-1)/2 << '\n',0;
	ll bh=0;
	for(int i=2; i<=n+m ;i++){
		if(v[i].size()==1) ans+=(cnt-(++bh));
	}
	for(int i=2; i<=n+m ;i++){
		if(v[i].size()==1) continue;
		int l=v[i][1],r=v[i].back();
		if(l==r) ans++;
		for(int j=i+1; j<=n+m ;j++){
			if(c[l][j-l]!='.') l++;
			if(c[r+1][j-r-1]=='.') r++;
			if(l==r && v[j].size()!=1) ans++;  
		}
	}
	for(int i=2; i<=n+m ;i++){
		if(v[i].size()==1) continue;
		int l=v[i][0],r=v[i][v[i].size()-2];
		for(int j=i+1; j<=n+m ;j++){
			if(c[l][j-l]!='.') l++;
			if(c[r+1][j-r-1]=='.') r++;
			if(l==r && v[j].size()!=1) ans++;  
		}
	}
	cout << ans << '\n';
}
posted @ 2020-12-06 16:40  Moodshadow  阅读(505)  评论(0编辑  收藏  举报