CF1372E

· · 题解

给出一种最为好写好想、实现细节最少的写法。

题意

给你一个 N \times M 的矩阵,其中每行被分隔成若干个区间。你需要在矩阵中填 1 或 0,一个区间只能有 1 个 1。现在你需要让每一列的 1 的数量的平方和最大。求出这个值。

分析

一个应该想到的性质:我们应该尽可能让 $1$ 在一列摞起来。这样,平方和更大。事实上,样例就是一个很好的例子。那我们的状态可以定义为: $dp_{i,j}$ 代表在横坐标 $[i,j]$,纵坐标 $[1,N]$ 的子矩阵内,最大列平方和。 枚举 $k \in [i,j]$。因为我们要让 $1$ 集中来加大平方和,因此我们要求 **所有左右端点均在 $[l,r]$ 内且经过 $k$ 的区间,都把 $1$ 放在 $k$ 上。** 之后,答案就是 $$dp_{i,j}= dp_{i,k-1}+cov_{i,j,k}+dp_{k+1,j}$$ 答案即可求出。容易发现,这里的时间复杂度是 $O(N^3)$ (我们认为 $N,M$ 相同)。 如何求出 $cov_{i,j,k}$?目前题解区有 $2$ 种解法:二维差分与前缀和、开 $l,r$ 数组存端点等方法。它们的时间复杂度分别是 $O(N^3)-O(N^3)$、$O(N^3)-O(N^4)$(破折号前是预处理复杂度,后面是 dp 复杂度)。 但是其实可以直接在输入时直接预处理。对于输入的区间 $[la,ra]$,枚举所有 $lb \in [1,la],rb \in [ra,M],k \in [la,ra]$。之后把 $cov_{lb,rb,k}$ 更新即可。对于单个区间,它实现了最劣 $O(N^3)$ 的预处理。一共有 $N^2$ 量级的区间,但因为所有 $k$ 之和一定 $=N^2$,所以复杂度是 $O(N^4)$ 而不是 $O(N^5)$。 这样实现了 $O(N^4)-O(N^3)$ 的解法,可以通过本题。 ```cpp #include <bits/stdc++.h> #define LL long long using namespace std; const int MAXN=505,MOD=998244353; int N,M; LL cov[105][105][105]; LL dp[105][105]; int main() { scanf("%d %d",&N,&M); for(int i=1;i<=N;i++) { int t; scanf("%d",&t); for(int j=1;j<=t;j++) { int la,ra; scanf("%d %d",&la,&ra); for(int lb=la;lb;lb--) for(int rb=ra;rb<=M;rb++) for(int k=la;k<=ra;k++) cov[lb][rb][k]++; } } for(int i=1;i<=M;i++) dp[i][i]=cov[i][i][i]*cov[i][i][i]; for(int i=1;i<=M;i++) dp[i][i-1]=0; for(int len=2;len<=M;len++) { for(int l=1;l+len-1<=M;l++) { int r=l+len-1; for(int i=l;i<=r;i++) { dp[l][r]=max(dp[l][r],cov[l][r][i]*cov[l][r][i]+dp[l][i-1]+dp[i+1][r]); } } } printf("%lld\n",dp[1][M]); return 0; } ```