专栏文章

CF2053F Earnest Matrix Complement 题解

CF2053F题解参与者 2已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@miqmq0pd
此快照首次捕获于
2025/12/04 07:19
3 个月前
此快照最后确认于
2025/12/04 07:19
3 个月前
查看原文
Problem:\rm Problem:Earnest Matrix Complement

题意

给定一个 n×mn\times m 的矩阵 AA,每个数在 [1,k][1,k] 间,其中有一些数待填。
定义该矩阵的权值为:
xi=1n1cntx,icntx,i+1\sum_x\sum_{i=1}^{n-1}cnt_{x,i}cnt_{x,i+1}
其中 cntx,icnt_{x,i} 表示 xx 在第 ii 行出现的次数。
求填完数后该矩阵权值最大值。
1n,m2×105,1kn×m6×1051\le n,m\le2\times10^5,1\le k\le n\times m\le6\times10^5

做法

首先发现每行只会填一种数,否则替换成上下两行出现次数总和最多的数一定不劣。
先把已知的数的贡献算出来,然后只需要考虑填的数造成的影响。
不难得到朴素 dp 方式:
fi,jf_{i,j} 表示考虑前 ii 行,第 ii 行填的数是 jj 的方案数。
不难发现 fi,jf_{i,j} 只会从 fi1,jf_{i-1,j}fi1f_{i-1} 中的最大值转移过来,设这个最大值是 mxi1mx_{i-1},第 ii 行的待填数有 empiemp_i 个,那么可以得到转移式:
fi,j=max(fi1,j+empiempi1,mxi1)+empi(cnti1,j+cnti+1,j)f_{i,j}=\max(f_{i-1,j}+emp_iemp_{i-1},mx_{i-1})+emp_i(cnt_{i-1,j}+cnt_{i+1,j})
(这里的 cnti,jcnt_{i,j} 表示在输入给出的已知数中,jj 在第 ii 行出现的次数)
式子可能有点长,我们拆成两步看:
fi,j=max(fi1,j+empiempi1,mxi1)f_{i,j}=\max(f_{i-1,j}+emp_iemp_{i-1},mx_{i-1})
fi,j:=fi,j+empi(cnti1,j+cnti+1,j)f_{i,j}:=f_{i,j}+emp_i(cnt_{i-1,j}+cnt_{i+1,j})
发现第一步转移对所有的 jj 都一样,可以直接全局加,全局取 max\max
第二步总转移次数是上下两行出现数字个数之和,也就是一共 O(nm)O(nm) 次单点加。
所以只要维护一个能支持全局加,全局取 max\max,单点加,求全局 max\max 的东西就可以了,可以用线段树之类的东西实现,不过打个全局标记就可以了,这样每次就是 O(1)O(1) 的,具体可以看代码。

代码

一个能支持全局加,全局取 max\max,单点加,求全局 max\max 的东西:
CPP
namespace A{
	ll addtag,maxtag;
	ll a[600001];
	ll mx;
	void clear()
	{
		for(int i=1;i<=k;++i)a[i]=0;
		addtag=maxtag=mx=0;
	}
	void alladd(cll x)
	{
		addtag+=x;
		maxtag+=x;
	}
	void allmax(cll x)
	{
		maxtag=max(maxtag,x);
	}
	void add(cint x,cll y)
	{
		a[x]=max(a[x],maxtag-addtag);
		a[x]+=y;
		mx=max(mx,a[x]);
	}
	ll ask()
	{
		return max(maxtag,mx+addtag);
	}
}
dp 部分:
CPP
for(int i=1;i<=n;++i)
{
	cll lstans=A::ask();
	A::alladd(1ll*emp[i]*emp[i-1]);
	A::allmax(lstans);
	for(int x:a[i-1])
	{
		if(x==-1)continue;
		A::add(x,emp[i]);
	}
	if(i<n)
	{
		for(int x:a[i+1])
		{
			if(x==-1)continue;
			A::add(x,emp[i]);
		}
	}
}
提交记录:

评论

2 条评论,欢迎与作者交流。

正在加载评论...