题解 AGC033D【Complexity】
problem
定义一个 0/1 矩阵 \(B\) 的复杂度为:
- 若 \(B\) 只由一种数字组成,其复杂度为 \(0\)。
- 否则,用一条平行于矩阵 \(B\) 任意一边的直线将 \(B\) 划分为两部分,则复杂度为所有划分方案中 两个子矩阵的复杂度的最大值加一 的最小值。
求出一个 \(n\times m\) 的 0/1 矩阵 \(A\) 的复杂度。\(n,m\leq 200\)。
solution 0
naive 的 DP:令 \(f(l,r,d,u)\) 表示 \(A\) 的一个子矩阵取 \(i\in[l,r],j\in[d,u]\) 的部分的复杂度。转移枚举直线,\(O(n^5)\)。
solution 1
考虑:
- 答案的下界:每次对半分矩阵,形成一棵满二叉树,深度 \(\log n+\log m=\log nm\)。
- \(f\) 随便拎出一维,都有单调性,显然的。
令 \(f(ans,l,r,d)\) 表示,当我们固定了答案为 \(ans\),已知 \([l,r],d\) 求最大的 \(u\)(有 LGP3147 内味了)。
对半分转移:假如我们求出的 \(u\) 给它加一,则 \(f_{ans,l,r,d}=f_{ans-1,l,r,f_{ans-1,l,r,d}}\),细节平凡,\(ans=0\) 显然随意二分。
中间劈开两半的转移,可以枚举中间点,变成两条曲线的 \(\max\),目测可以随意二分,这时复杂度是 \(O(n^3\log^2 n)\)。
solution 2
我们再开一个 \(g_{ans,d,u,l}\),和 \(f\) 差不多,方向反过来。对半分的转移一样。
对于中间劈开两半的转移,你固定 \(l,d\),相当于一个 \(g_{d,u,l}\) 可以对所有 \(f_{l,[l\leq r\leq g_{d,u,l}],d}\) 作贡献,反过来也是。
从小到大枚举 \(u\),由于 \(g_{d,u,l}\) 单调,相当于每个决策更新的状态是一段平摊线性的区间,直接扫即可。\(O(n^3\log n)\)
code
建议手推一遍,意会一下。
需要对 \(ans\) 滚动数组。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
//#undef LOCAL
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int n,m,a[310][310],f[2][310][310][310],g[2][310][310][310],crx;
bool check(int l,int r,int d,int u){int s=a[r][u]-a[l-1][u]-a[r][d-1]+a[l-1][d-1]; return !s||s==(r-l+1)*(u-d+1);}
int A[310],B[310];
int dp(){
//f[0],g[0]
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++){
f[0][l][r][m+1]=f[1][l][r][m+1]=m+1;
for(int d=1;d<=m;d++){
int&res=f[crx][l][r][d]=d;
for(int L=d,R=m,mid=(L+R)>>1;L<=R;mid=(L+R)>>1){
if(check(l,r,d,mid)) res=mid+1,L=mid+1;
else R=mid-1;
}
}
}
for(int d=1;d<=m;d++)
for(int u=d;u<=m;u++){
g[0][d][u][n+1]=g[1][d][u][n+1]=n+1;
for(int l=1;l<=n;l++){
int&res=g[crx][d][u][l]=l;
for(int L=l,R=n,mid=(L+R)>>1;L<=R;mid=(L+R)>>1){
if(check(l,mid,d,u)) res=mid+1,L=mid+1;
else R=mid-1;
}
}
}
if(f[crx][1][n][1]>m) return 0;
crx^=1;
//f[k],g[k]
for(int k=1;k<=17;k++,crx^=1){
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++)
for(int d=1;d<=m;d++)
f[crx][l][r][d]=f[crx^1][l][r][f[crx^1][l][r][d]];
for(int d=1;d<=m;d++)
for(int u=d;u<=m;u++)
for(int l=1;l<=n;l++)
g[crx][d][u][l]=g[crx^1][d][u][g[crx^1][d][u][l]];
for(int l=1;l<=n;l++)
for(int d=1;d<=m;d++){
for(int r=l;r<=n;r++) A[r]=f[crx][l][r][d];
for(int u=d;u<=m;u++) B[u]=g[crx][d][u][l];
A[n+1]=d,B[m+1]=l;
for(int r=l;r<=n;r++){
for(int u=A[r]-1;u>=A[r+1];u--) g[crx][d][u][l]=max(g[crx][d][u][l],r+1),debug("trans: g[%d][%d][%d]<=f[%d][%d][%d]\n",d,u,l,l,r,d);
//for(int u=A[r]-1;u>=l;u--) 是暴力
}
for(int u=d;u<=m;u++){
for(int r=B[u]-1;r>=B[u+1];r--) f[crx][l][r][d]=max(f[crx][l][r][d],u+1),debug("trans: f[%d][%d][%d]<=g[%d][%d][%d]\n",l,r,d,d,u,l);
}
}
if(f[crx][1][n][1]>m) return k;
}
return -1;
}
int main(){
#ifndef LOCAL
freopen("painting.out","w",stdout);
freopen("painting.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
char s[1010]; scanf("%s",s+1);
for(int j=1;j<=m;j++) a[i][j]=s[j]=='.';
for(int j=1;j<=m;j++) a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
printf("%d\n",dp());
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-agc033d.html