[AGC033D] Complexity
链接
题目大意
给定一个 \(n\times m\) 的矩阵。
定义一个矩阵的元素如果全部相等那么他的“混乱度”为0。否则你可以用一条直线将其分为两个子矩阵,混乱度为两个子矩阵的混乱度较大值+1。
\(n\leq 185\)
题解
首先显然有一个 \(O(n^4\log n)\) 的dp:令 \(f_{a,b,c,d}\) 表示子矩阵的混乱度。每次二分加单调队列得到结果。
但是这样显然过不去,考虑优化。首先这个状态数显然不行。
我们分析最后的答案。可以发现,我们可以每次先按x的中点切,再按y的中点切,这样最坏也是 \(\log n+\log m\) 的。
那么我们不妨枚举这样一个答案作为转移步数。然后令转移状态 \(f_{l,r,k}\) 表示当前的转移步数下 \(x\) 轴在 \([l,r]\) 中,\(y\) 轴左端点为 \(k\) 时 \(y\) 轴最右端能到哪里。
考虑转移。对于 \(y\) 轴可以直接套用类似于倍增的思路,即 \(f_{l,r,k}=f'{l,r,f'{l,r,k}}\)。\(O(1)\) 转移。
然后对于 \(x\) 轴,可以发现它的函数 \(\max(f_{l,m,k},f_{m+1,r,k})\) 中两个子函数一个单增一个单减。
那么很显然哪边大往哪边靠一定较优,所以符合二分。转移复杂度 \(O(\log n)\)。
当然还有 \(O(1)\) 双指针转移。不过这里 \(O(\log n)\) 也能通过。总复杂度 \(O(n^2m\log (nm)\log n)\)。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 210
using namespace std;
char s[N][N];
int f[2][N][N][N];
int main()
{
int n,m,u=1;
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++) scanf("%s",s[i]+1);
for(int j=1;j<=m;j++)
for(int i=j;i;i--)
for(int k=n;k;k--)
if(i==j) f[u][i][j][k]=s[i][k]==s[i][k+1]?f[u][i][j][k+1]:k+1;
else f[u][i][j][k]=s[j-1][k]==s[j][k]?min(f[u][i][j-1][k],f[u][j][j][k]):k;
for(int ans=0;ans<=n;ans++)
{
if(f[u][1][m][1]==n+1){printf("%d\n",ans);return 0;}
int p=u;u=!u;
for(int lf=1;lf<=m;lf++)
for(int rf=lf;rf<=m;rf++)
for(int k=1;k<=n;k++)
{
if(f[p][lf][rf][k]==n+1) f[u][lf][rf][k]=n+1;
else f[u][lf][rf][k]=f[p][lf][rf][f[p][lf][rf][k]];
for(int l=lf,r=rf;l<=r;)
{
int mid=(l+r)>>1;
f[u][lf][rf][k]=max(f[u][lf][rf][k],min(f[p][lf][mid][k],f[p][mid+1][rf][k]));
if(f[p][lf][mid][k]>f[p][mid+1][rf][k]) l=mid+1;
else r=mid-1;
}
}
}
return 0;
}
本文来自博客园,作者:Flying2018,转载请注明原文链接:https://www.cnblogs.com/Flying2018/p/13883370.html

浙公网安备 33010602011771号