ARC214F Sol

前置题目:ABC413G & ABC239G

绿+紫+一点改动 = ?
——黑!

强强?!题目。

首先应用一下 ABC413G 的内容,可以发现能走到的不充分但必要条件就是满足不存在从右上到左下的 # 的连通块(八连通)。

但是,同时我们发现,这个不能够连续走的限制也就是让形如 #.# 的无法直接从中间走过。

因此这题变成了一种更为猎奇的图论:考虑对于 # 进行连边,根据上面的内容,可以发现实际上就是对于曼哈顿距离小于等于 2# 进行连边。起点考虑与右上的 # 进行连边,终点则是对于左下的进行连边。这里需要注意的是,第二行的 # 也可以算作连边,因为其和上面的边界一起组成了 #.# 的结构,对于倒数第二行,第二列,倒数第二列也要进行这样的考虑。

具体的,就是代码的这一部分:

if(i == 1 || j == W || (i == 2 && j != 1) || ( j == W - 1&& i != H)) flow.Add(flow.S,id[i][j],INF);
if(j == 1 || i == H || (j == 2 && i != 1) || ( i == H - 1 && j != W)) flow.Add(id[i][j]+tot,flow.T,INF);

那么,最终的方案能够可行,当且仅当不存在从起点到终点的通路。

因此问题变成求最小割。可以使用 ABC239G 的内容进行解决。

这里给出这一部分简化题解:

考虑把一个点拆成 u 和 u+n,u用来接入边,u+n 用来接出边,对于 u -> u+n 连一条权值为 1 的边,原图的边权值设为 inf,表示不可以割去。然后跑最小割就好了。

Code

代码纯一坨,谨慎阅读。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 105;
const int M = 40000 + 10;
const int K = 6e5 + 10;
const LL INF = 1e15;
const int inf = 1e9 + 7;
int T;
char c[N][N];
int id[N][N];
int H,W;
int nx[12] = {0,0,1,-1,1,-1,-1,1,2,-2,0,0};
int ny[12] = {1,-1,0,0,1,-1,1,-1,0,0,2,-2};
vector<int> G[N*N];
struct max_flow{
	int head[M],now[M];
	struct edge{
		int to,nxt;
		LL val;
	};
	edge G[K];
	int S,T;
	int dep[M];
	int tot = 1;
	int n;
	void add(int u,int v,LL w){
		tot ++ ;
		G[tot].nxt = head[u];
		G[tot].to = v;
		G[tot].val = w;
		head[u] = tot;
	}
	void Add(int u,int v,LL w){
		add(u,v,w);
		add(v,u,0);
	}
	bool bfs(){
		queue<int> q;
		q.push(S);
		for(int i=1;i<=n;i++) dep[i] = inf;
		dep[S] = 0;
		now[S] = head[S];
		while(!q.empty()){
			int u = q.front();
			q.pop();
			for(int i = head[u];i;i=G[i].nxt){
				int v = G[i].to;
				LL w = G[i].val;
				if(dep[v] == inf && w){
					dep[v] = dep[u] + 1;
					q.push(v);
					now[v] = head[v];
					if(v == T) return 1;
				}
			}
		}
		return 0;
	}
	LL dfs(int u,LL sum){
		if(u == T) return sum;
		LL flow = 0;
		for(int i=now[u];i && sum;i=G[i].nxt){
			now[u] = i;
			int v = G[i].to;
			LL w = G[i].val;
			if(dep[v] == dep[u] + 1 && w){
				LL k = dfs(v,min(sum,w));
				if(k == 0) dep[v] = inf;
				sum -= k;
				flow += k;
				G[i].val -= k;
				G[i^1].val += k;
			}
		}
		return flow;
	}
	LL query(){
		LL ans = 0;
		while(bfs()) ans += dfs(S,INF);
		return ans ;
	}
}flow;
void solve(){
	cin >> H >> W;
	int tot = 0;
	for(int i=1;i<=H;i++)
		for(int j=1;j<=W;j++){
			cin >> c[i][j];
			if(c[i][j] == '#') id[i][j] = ++tot;
		}
	for(int i=1;i<=H;i++){
		for(int j=1;j<=W;j++){
			if(c[i][j] == '.') continue;
			for(int l=0;l<12;l++){
				int tx,ty;
				tx = i + nx[l];
				ty = j + ny[l];
				if(tx < 1  || tx > H || ty < 1 || ty > W || c[tx][ty] == '.') continue;
				// cout << i << "," << j << " " << tx << "," << ty << "\n";
				G[id[i][j]].push_back(id[tx][ty]);
			}
		}
	}
	flow.n = tot * 2 + 2;
	flow.S = tot * 2 + 1;
	flow.T = tot * 2 + 2;
	for(int i=1;i<=H;i++){
		for(int j=1;j<=W;j++){
			if(c[i][j] == '#'){
				if(i == 1 || j == W || (i == 2 && j != 1) || ( j == W - 1&& i != H)) flow.Add(flow.S,id[i][j],INF);
				if(j == 1 || i == H || (j == 2 && i != 1) || ( i == H - 1 && j != W)) flow.Add(id[i][j]+tot,flow.T,INF);
			}
		}
	}
	for(int i=1;i<=tot;i++){
		flow.Add(i,i+tot,1);
		for(int x : G[i]){
			flow.Add(i+tot,x,INF);
		}
	}
	cout << flow.query() << "\n";
	for(int i=1;i<=flow.n;i++)
		flow.head[i] = 0;
	flow.tot = 1;
	for(int i=1;i<=tot;i++)
		G[i].clear();
}
int main()
{
	IOS;
	cin >> T;
	while(T -- ) solve();
	return 0;
}
posted @ 2026-02-25 14:50  WinterXorSnow  阅读(13)  评论(0)    收藏  举报