1 条题解
-
0
Description
有一个 的数表 ,。
现在将这个数表分成两个数表 ,使得 最小。同时输出分割的方法。
Analysis
题目要求 最小,同时又知道 固定,所以我们要求最小的 ,使得 和 的值尽量接近,这样 才能尽可能的小。
Solution
不妨以竖切为例,横切同理。
设 表示前 列的和,在 行后切最优,则 ,所以 $\lvert \sum x-\sum y\rvert=\lvert 2 \times L(i)-L(m)\rvert$。又知 为定值, 单调上升,所以 的值就是一个单调上升的值减去一个定值的绝对值。这不就是单谷函数吗?
所以本题可以用三分求解。不会三分请左转。
三分过程不再详细赘述,现在先解决一个问题,如何在短时间内求出 呢?我想到过用前缀和预处理再 查询,但预处理的时间和空间复杂度都很大,于是果断放弃。
我们可以简单地推一个式子。先拿 的数表举例:
$$\begin{bmatrix}1&2&3&4&5\\6&7&8&9&10\\11&12&13&14&15\end{bmatrix}$$我们试求一下 。第 列的数组成等差数列,首项为 ,项数为 ,公差为 ,所以末项为 。用等差数列求和公式即可得出这一列的和为 。第 列中每一个数都比第 列中和自己同一行的数大 ,又因为每一列有 个数,所以第 列的总和比第 列大 。同理,第 列的总和比第 列大 。所以 列总和即为 ,即 。
推广至在 的数表中求 ,不难得出如下代码:
int Cal_L(int x){//求L(x) int L1=(n*m-m+2)*n/2;//L1=(1+1+m*(n-1))*n/2=(n*m-m+2)*n/2 int ans=L1*x+x*(x-1)/2*n;//Lx=L1*x+(1到x-1之和)*n=L1*x+(x-1)*x/2*n return ans; }所有问题就迎刃而解了。
横切的思考方式同理,留与读者自行推导。
Code
#include<bits/stdc++.h> #define int long long using namespace std; int T,n,m; int Cal_L(int x){//求L(x) int L1=(n*m-m+2)*n/2;//L1=(1+1+m*(n-1))*n/2=(n*m-m+2)*n/2 int ans=L1*x+x*(x-1)/2*n;//Lx=L1*x+(1到x-1之和)*n=L1*x+(x-1)*x/2*n return ans; } int Cal_H(int x){//同理 int H1=(1+m)*m/2; int ans=H1*x+x*(x-1)/2*m*m; return ans; } signed main(){ cin>>T; while(T--){ cin>>n>>m; //以下就是三分板子力 int lt=1,rt=m; while(lt<rt){ int lmid=lt+(rt-lt+1)/3,rmid=lt+(rt-lt+1)*2/3; int lans=abs(Cal_L(lmid)*2-Cal_L(m)); int rans=abs(Cal_L(rmid)*2-Cal_L(m)); if(lans<=rans) rt=rmid-1; else lt=lmid+1; } int Lans=abs(Cal_L(rt)*2-Cal_L(m)),Lpos=rt; lt=1,rt=n; while(lt<rt){ int lmid=lt+(rt-lt+1)/3,rmid=lt+(rt-lt+1)*2/3; int lans=abs(Cal_H(lmid)*2-Cal_H(n)); int rans=abs(Cal_H(rmid)*2-Cal_H(n)); if(lans<=rans) rt=rmid-1; else lt=lmid+1; } int Hans=abs(Cal_H(rt)*2-Cal_H(n)),Hpos=rt; if(Lans<=Hans)//比较竖切横切谁更优 cout<<"V "<<Lpos+1<<"\n"; else cout<<"H "<<Hpos+1<<"\n"; } return 0; }
- 1
信息
- ID
- 10277
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者