CF1592F Alice and Recoloring

题目

有一个 \(n\times m\) 的矩阵,位置 \((i,j)\) 上的颜色是白色或者黑色,最小化变成全白的代价,操作如下:

  • 反转一个包含 \((1,1)\) 的子矩阵,花费为 \(1\)
  • 反转一个包含 \((n,1)\) 的子矩阵,花费为 \(2/3\)
  • 反转一个包含 \((1,m)\) 的子矩阵,花费为 \(4\)
  • 反转一个包含 \((n,m)\) 的子矩阵,花费为 \(3/2\)

\(n,m\leq 500\)

Easy version

容易发现第 \(2,3\) 种操作一定没用,因为它们都可以被一操作替换。

因为反转操作是范围修改,我们尝试使用类似差分的技巧让它修改某个单点值,把白色当成 \(0\),把黑色当成 \(1\),设 \(b_{i,j}=a_{i,j}\oplus a_{i,j+1}\oplus a_{i+1,j}\oplus a_{i+1,j+1}\),最后的状态是全为 \(0\)

那么操作 \(1\) 相当于反转 \(b_{i,j}\),操作 \(4\) 相当于反转 \(b_{i-1,m},b_{n,j-1},b_{i-1,j-1},b_{n,m}\)

根据贪心当且仅当四个位置都有值的时候才会使用 \(4\) 操作,否则使用 \(1\) 操作是最优的,那么我们暴力判断有没有这样的位置 \((i,j)\),如果没有答案就是 \(b_{x,y}=1\) 的个数,否则答案需要减去 \(1\)

Hard version

因为 \(4\) 操作的代价变小了,所以它的出场机会会变多,不妨先不考虑 \(b_{n,m}\)(影响太小)

那么如果 \(b_{i-1,m},b_{n,j-1},b_{i-1,j-1}\) 三个位置都有值,就可以使用 \(4\) 操作来减少 \(1\) 的代价,否则都不能减少代价。很容易看出这是一个行和列的匹配问题,我们找到最大匹配就是最大代价减少量。

这时候再来考虑 \(b_{n,m}\),如果匹配个数是奇数,若 \(b_{n,m}=0\) 则需要多花费 \(1\) 的代价(但是暴力匹配还是最优的),若 \(b_{n,m}=0\) 则可以减少 \(1\) 的代价,如果匹配个数为偶数则无影响。

总结

本题最关键的技巧是广义差分,一维数组上我们通过差分可以把区间修改变成单点修改。在矩阵上同样可以使用差分技巧,那么可以把矩阵操作变成单点操作,用四个相邻位置来定义差分值即可。

//I might be staring at my last chance
#include <cstdio>
const int M = 505;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,a[M][M],b[M][M];char s[M][M];
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s[i]+1);
		for(int j=1;j<=m;j++)
			a[i][j]=s[i][j]=='B';
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			b[i][j]=a[i][j]^a[i+1][j]^
			a[i][j+1]^a[i+1][j+1];
			if(b[i][j]) ans++;
		}
	int sub=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(b[n][m] && b[i-1][m]
			&& b[n][j-1] && b[i-1][j-1])
				sub=1;
	printf("%d\n",ans-sub);
}
//I might be staring at my last chance
#include <cstdio>
#include <iostream> 
#include <queue>
using namespace std;
const int M = 1005;
const int inf = 0x3f3f3f3f;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,tot,f[M],a[M][M],b[M][M];
int S,T,mt,dis[M],cur[M];char s[M][M];
struct edge{int v,c,next;} e[M*M];
void add(int u,int v,int c)
{
	e[++tot]=edge{v,c,f[u]},f[u]=tot;
	e[++tot]=edge{u,0,f[v]},f[v]=tot;
}
int bfs()
{
	for(int i=0;i<=T;i++) dis[i]=0;
	queue<int> q;
	q.push(S);dis[S]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		if(u==T) return 1;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v;
			if(!dis[v] && e[i].c>0)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return 0;
}
int dfs(int u,int ept)
{
	if(u==T) return ept;
	int flow=0,tmp=0;
	for(int &i=cur[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(dis[v]==dis[u]+1 && e[i].c>0)
		{
			tmp=dfs(v,min(ept,e[i].c));
			if(!tmp) continue;
			ept-=tmp;
			e[i].c-=tmp;
			e[i^1].c+=tmp;
			flow+=tmp;
			if(!ept) break;
		}
	}
	return flow;
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s[i]+1);
		for(int j=1;j<=m;j++)
			a[i][j]=s[i][j]=='B';
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			b[i][j]=a[i][j]^a[i+1][j]^
			a[i][j+1]^a[i+1][j+1];
			if(b[i][j]) ans++;
		}
	S=0;T=n+m+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(b[i-1][m] && b[n][j-1] && b[i-1][j-1])
				add(i,n+j,1);
	for(int i=1;i<=n;i++)
		add(S,i,1);
	for(int i=1;i<=m;i++)
		add(i+n,T,1);
	while(bfs())
	{
		for(int i=0;i<=T;i++)
			cur[i]=f[i];
		mt+=dfs(S,inf);
	}
	if(mt%2)
	{
		if(a[n][m]==0) ans++;
		else ans--;
	}
	printf("%d\n",ans-mt);
}
posted @ 2021-10-09 17:48  C202044zxy  阅读(40)  评论(0编辑  收藏  举报