CF1592F2 Alice and Recoloring 2

目前在看贪心/构造/DP 杂题选做,发现一道非常不错的结论题,具有启发意义。

先说明如下结论

结论一:如何怎么样都不会使用二和三操作
证明:
二三操作显然可以通过两次一操作达到,而其操作费用大于两次一操作费用

所以显然我们只会操作一四操作。

那么我们发现翻转一整个矩形不好操作,翻转到一特定状态也不好处理,不如考虑倒序操作:即给定特定状态变为全白状态的费用,知其与原问题等价。

然后我们处理翻转问题,我们考虑一个转换
\(a_{i,j} = a_{i,j}\ xor\ a_{i+1,j}\ xor\ a_{i,j + 1}\ xor\ a_{i + 1,j + 1}\)
我们发现全白时显然全等为\(0\)

而一四操作我们可以将其本质变为:

一操作:单点翻转\((x,y)\)

四操作:四点翻转\((x,y),(x,m),(n,y),(n,m)\)

那么问题变得显然起来。

我们依旧给出了两个结论

结论二:不用同时使用两个横坐标或竖坐标相等的四操作
证明:其等价于修改四个任意散点,其可以等价于四次一操作的费用

结论三:除非 \((x,y),(n,y),(x,m)\) 都为1,才会使用 \((x,y)\) 这一四操作
证明:如果有一个不为\(1\),那么其有一个错误反转,我们需要其一个一操作反转回来,那么其等价于\(1 + 2 = 3\),可以使用一操作代替。

那么经典问题转化为满足某种条件的改变行列取点数量。

二分图上最大匹配即可。

#include<iostream>
#include<cstdio>
#include<queue>
#define ll long long 
#define N 30000

int n,m;

int head[N];

int cnt = 1;

struct P{
	int to,next;
	ll v;
}e[N << 2];

inline void add(int x,int y,int v){
	e[++cnt].to = y;
	e[cnt].v = v;
	e[cnt].next = head[x];
	head[x] = cnt;
}

std::queue<int>QWQ;
ll dis[N];
bool vis[N];

int a[505][505];

int s,t;

inline bool bfs(){
	for(int i = 1;i <= n + m + 2;++i)
	dis[i] = 1e10,vis[i] = 0;
	dis[s] = 0;
	QWQ.push(s);
	vis[s] = 1;
	while(!QWQ.empty()){
		int u = QWQ.front();
		QWQ.pop();
		vis[u] = 0;
		for(int i = head[u];i;i = e[i].next){
			int v = e[i].to;
			if(dis[v] > dis[u] + 1 && e[i].v){
				dis[v] = dis[u] + 1;
				if(!vis[v])
				vis[v] = 1,QWQ.push(v);
			}
		}
	}
	return dis[t] != 1e10;
}

inline ll dfs(int u,ll in){
	ll out = 0;
	if(u == t)
	return in;
	for(int i = head[u];i;i = e[i].next){
		int v = e[i].to;
		if(e[i].v && dis[v] == dis[u] + 1){
			int to = dfs(v,std::min(e[i].v,in));
			e[i].v -= to;
			e[i ^ 1].v += to;
			in -= to;
			out += to;
		}
	}
	if(out == 0)
	dis[u] = 0;
	return out;
}

inline int dinic(){
	int ans = 0;
	while(bfs())
	ans += dfs(s,1e18);
	return ans;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
		char s = getchar();
		while(s != 'W' && s != 'B')
		s = getchar();
		a[i][j] = (s == 'B');
		s = '.';
	}	
	int ans = 0;
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
	a[i][j] = a[i][j] ^ a[i + 1][j] ^ a[i + 1][j + 1] ^ a[i][j + 1];
	}
//	for(int i = 1;i <= n;++i,puts(""))
//	for(int j = 1;j <= m;++j)
//	std::cout<<a[i][j]<<" ";
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j){
		if(a[i][m] && (a[n][j] && a[i][j])){
			add(i,n + j,1);
			add(n + j,i,0);
		}
	}
	s = n + m + 1,t = n + m + 2;
	for(int i = 1;i < n;++i)
	add(s,i,1),add(i,s,0);
	for(int j = 1;j < m;++j)
	add(n + j,t,1),add(t,n + j,0);
	int k = dinic();
	a[n][m] = a[n][m] ^ (k & 1);
	for(int i = 1;i <= n;++i)
	for(int j = 1;j <= m;++j)
	ans += a[i][j];
	std::cout<<ans - k<<std::endl;
}//注意建图后的点集大小
原文地址:https://www.cnblogs.com/dixiao/p/15628595.html