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;
}
总结:
- 遇到数学式子注意转化
- 大胆猜想,小心证明!!!

浙公网安备 33010602011771号