CF2194D Table Cut 题解

划分成两块使得两块\(1\)的数量\(a,b\)的乘积\(ab\)最大。见到这个式子我们不难想到高中学的不等式\(ab \le(\frac{a+b}{2})^2\),取等时候当且仅当\(a=b\)成立,所以说只要\(a\)\(b\)的值越接近,那么它们的乘积就越大。

设原图上共有\(s\)\(1\),感性理解一下我们总可以把原图划分称为两部分,下部分我们强制让它包含\(\lfloor\frac{s}{2}\rfloor\)\(1\),上部分强制让它包含\(s-\lfloor\frac{s}{2}\rfloor\)\(1\)。那么答案就是\(\lfloor\frac{s}{2}\rfloor(s-\lfloor\frac{s}{2}\rfloor)\)。接下来我们考虑怎么去构造路径,考虑枚举每一列,如果当前这一列所在的这一行加上之前已经积累的\(1\)的数量超过了\(\lfloor\frac{s}{2}\rfloor\),我们就向下减少\(1\)的数量直到总共\(1\)的数量为\(\lfloor\frac{s}{2}\rfloor\)为止。每一列我们可以用前缀和维护\(1\rightarrow i\)行内的\(1\)的数量。注意最后有可能出现走到\(m\)列但是不在\(1\)行就已经达到了\(\lfloor\frac{s}{2}\rfloor\)的情况,所以说最后我们需要让它强制走到\(1\)行。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
/*



*/
#define ll long long 
void sol() {
    int n,m;
    cin>>n>>m;
    vector<vector<ll>> mp(n+1,vector<ll>(m+1,0));
    ll tot=0;
    for(int i=n;i>=1;i--)
        for(int j=1;j<=m;j++){
            cin>>mp[i][j];
            tot+=mp[i][j];
        }

    for(int j=1;j<=m;j++){
        for(int i=1;i<=n;i++){
            mp[i][j]+=mp[i-1][j];
        }
    }

    ll low=tot/2,sum=0;
    int cur=n;
    cout<<low*(tot-low)<<'\n';
    for(int j=1;j<=m;j++){
        while(cur>=1&&sum+mp[cur][j]>low){
            cur--;cout<<"D";
        }
        sum+=mp[cur][j];
        cout<<"R";
    }
    while(cur>=1){cur--;cout<<"D";}
    cout<<'\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t = 1;
    cin >> t;
    while (t--) {
        sol();
    }
    return 0;
}

总结:

  • 遇到数学式子注意转化
  • 大胆猜想,小心证明!!!
posted @ 2026-03-19 15:54  1445141  阅读(5)  评论(0)    收藏  举报