AT_agc033_d [AGC033D] Complexity

好题。

首先有一个显然的做法,设计 \(f_{i,j,x,y}\) 表示左上角是 \((i,j)\) 右下角是 \((x,y)\) 的最小答案,然后可以枚举矩形边长和分割线做到 \(O(n^5)\) 的复杂度,无法通过。

考虑优化以上做法,一个最劣的情况是所有位置周围的数字全部与当前位置不同,那么我们是每两个数字一合并最优,这只会合并 \(\log (nm)\) 轮,所以答案的上界只有 \(\log (nm)\)

发现了这个性质之后,我们就可以优化上述做法了,在一个大矩形枚举的时候,因为可能最优的转移位置只有 \(\log\) 个,所以枚举分割线可以省去,复杂度是 \(O(n^4\log n)\) 的,但是时间优化了空间不够。

再换一个想法,要优化空间就把答案扔到状态上,重新改一下状态,\(f_{k,u,l,r}\) 表示第 \(u\) 行及其往下,\([l,r]\) 这个范围内合并不超过 \(k\) 次所能到达的最大行数是多少。

这个状态就很巧了,对于答案的判定,我们枚举答案,然后看 \(f_{k,1,1,m}\) 是否能扩展到 \(n\) 即可。

\(k=0\) 开始看,我们首先要预处理出这个东西,考虑直接枚举 \(u,l\),然后从 \(l\) 开始枚举 \(r\),因为每次向右走的时候最大扩展位置一定单调不增,所以额外开一个变量每次减就好了,复杂度是 \(O(n^3)\),而且卡不满。

然后是转移,首先有 \(f_{k,u,l,r} \gets f_{k-1,u,l,r}\),然后考虑枚举行来分割的情况,最优的显然是这一部分 \(k-1\) 答案的下一行也拿 \(k-1\) 来扩展,写出来就是 \(f_{k,u,l,r} \gets f_{k-1,f_{k-1,u,l,r}+1,l,r}\)

对于枚举列来分割的情况,我们还是先枚举分割点 \(x\),那么最远扩展位置是 \(f_{k,u,l,r} \gets \min(f_{k-1,u,l,x},f_{k-1,u,x+1,r})\),之后对以上说的转移取 \(\max\) 就做完了。

时间复杂度 \(O(n^4 \log n)\),空间 \(O(n^3\log n)\),可以通过,可能需要卡卡常,我跑的还挺快的。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define pr putchar('\n')
#define fi first
#define se second
#define pp putchar(' ')
#define pii pair<ll,ll>
#define pdi pair<ll,ll>
#define mem(aa,bb) memset(aa,bb,sizeof(aa))
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
#define pb push_back
//#pragma GCC optimize(2)
using namespace std;
typedef int ll;
//#define pis pair<ll,char >
//typedef long long ll;
//typedef __int128 ll;
typedef double db;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
const ll N=1e4+5,M=2e4+5;
const ll base=131,mod=998244353;
ll f[17][186][186][186],n,m,pre[186][186];
char ch[186][186];
inline ll chk(ll x,ll y,ll l,ll r)
{
	return pre[l][r]-pre[x-1][r]-pre[l][y-1]+pre[x-1][y-1];
}
signed main(){
	read(n),read(m);
	fo(1,i,n) fo(1,j,m) cin>>ch[i][j];
	fo(1,i,n) fo(1,j,m) pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+(ch[i][j]=='#');
	fo(1,u,n) fo(1,l,m)
	{
		ll d=n;
		fo(l,r,m)
		{
			while(d>=u)
			{
				ll num=chk(u,l,d,r);
				if(!num||num==(r-l+1)*(d-u+1)) break;
				d--;
			}
			if(d<u) break;
			f[0][u][l][r]=d;
		}	
	}
	fo(1,k,16) fo(1,u,n) fo(1,l,m) fo(1,r,m)
	{
		f[k][u][l][r]=max({f[k][u][l][r],f[k-1][u][l][r],f[k-1][f[k-1][u][l][r]+1][l][r]});
		fo(l,x,r-1) f[k][u][l][r]=max({f[k][u][l][r],min(f[k-1][u][l][x],f[k-1][u][x+1][r])});	
	}	
	fo(0,k,16) if(f[k][1][1][m]==n) return wr(k),pr,0;
	return 0;
}
posted @ 2025-03-13 17:03  Wei_Han  阅读(15)  评论(0)    收藏  举报