非常好的构造题!山东省集被出了。
应该通俗易懂吧......
思路
考虑二分答案 m i d mid mid,设 L i , j = a i , j − m i d L_{i,j}=a_{i,j}-mid Li,j=ai,j−mid, R i , j = a i , j + m i d R_{i,j}=a_{i,j}+mid Ri,j=ai,j+mid,则 X i Y j ∈ [ L i , j , R i , j ] X_iY_j\in [L_{i,j},R_{i,j}] XiYj∈[Li,j,Ri,j]。我们只需要判断是否有符合条件的构造即可。
不妨设 X n ≤ Y m X_n\le Y_m Xn≤Ym( X n ≥ Y m X_n\ge Y_m Xn≥Ym 同理)埋下伏笔,设 S i S_i Si 为目前 X i X_i Xi 的最小值(初始为 1 1 1), D i D_i Di 为目前 Y i Y_i Yi 的最大值,初始为 2 × 10 9 2\times 10^9 2×109。考虑逼近法,逐渐逼近 X i X_i Xi 与 Y i Y_i Yi 的真实值。
我们考虑依次进行以下操作:
- 从前往后枚举 S i S_i Si,若 S i ≤ S i − 1 S_i\le S_{i-1} Si≤Si−1,则 S i ⟵ S i − 1 + 1 S_i\longleftarrow S_{i-1}+1 Si⟵Si−1+1。同时枚举 D j D_j Dj,若 S i D j < L i , j S_iD_j< L_{i,j} SiDj<Li,j,由于 D j D_j Dj 已经是 Y j Y_j Yj 的最大值了,所以只能令 S i ⟵ ⌈ L i , j D j ⌉ S_i \longleftarrow \left \lceil \frac{L_{i,j}}{D_j} \right \rceil Si⟵⌈DjLi,j⌉。
- 从后往前枚举 D i D_i Di,若 D i ≥ D i + 1 D_i\ge D_{i+1} Di≥Di+1,则 D i ⟵ D i + 1 − 1 D_i\longleftarrow D_{i+1}-1 Di⟵Di+1−1。同时枚举 S j S_j Sj,若 S j D i > R j , i S_jD_i> R{j,i} SjDi>Rj,i,由于 S j S_j Sj 已经是 X j X_j Xj 的最小值了,所以只能令 D i ⟵ ⌊ L j , i S j ⌋ D_i\longleftarrow \left \lfloor \frac{L_{j,i}}{S_j} \right \rfloor Di⟵⌊SjLj,i⌋。
- 若 S i S_i Si 与 D j D_j Dj 均符合题意,则 S i S_i Si 与 D j D_j Dj 是 X i X_i Xi 与 Y j Y_j Yj 的一组合法构造。
- 若 X n > Y m X_n>Y_m Xn>Ym 或 Y m ≤ 0 Y_m\le 0 Ym≤0,则直接返回不合法。
按照上述顺序依次操作构造出的序列显然是对的。下面我们证明一下操作的复杂度:
- 显然,每一轮操作中,至少存在一个 S i S_i Si 变大或 D j D_j Dj 变小。
- 显然,如果我们想让惩罚值最小, X i Y j X_iY_j XiYj 不能超过 10 9 10^9 109(否则我还不如直接给 X X X 赋值为 1 , 2 , ... , n 1,2,\dots ,n 1,2,...,n,将 Y Y Y 赋值为 1 , 2 , ... , m 1,2,\dots ,m 1,2,...,m)。
- 还记得前面说的吗?要求 X n ≤ Y m X_n\le Y_m Xn≤Ym,又由于 X i Y j ≤ 10 9 X_iY_j\le 10^9 XiYj≤109,所以 X i ≤ 10 9 ≈ 3 × 10 4 ≤ Y j X_i\le \sqrt{10^9}\approx 3\times 10^4 \le Y_j Xi≤109 ≈3×104≤Yj。则每个 X i X_i Xi 最多增加 3 × 10 4 3\times 10^4 3×104 次,每个 Y j Y_j Yj 最多减少 3 × 10 4 3\times 10^4 3×104 次。故总轮数不会超过 3 × 10 4 ( n + m ) ≈ 3 × 10 5 3\times 10^4(n+m)\approx 3\times 10^5 3×104(n+m)≈3×105。
证明完毕。
加上外面的二分与具体实现,总时间复杂度为 O ( ( n + m ) n m V log V ) O((n+m)nm\sqrt{V} \log_{}{V} ) O((n+m)nmV logV),其中 V = 2 × 10 9 V=2\times 10^9 V=2×109。(算下来不到 10 7 10^7 107)
代码
注意数组的第一维与第二维不要写反,不然调起来可能比较麻烦。
cpp
#include <bits/stdc++.h>
using namespace std;
long long n,m,a[10][10],l[10][10],r[10][10];
long long s[10],d[10];
bool check(long long x)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) l[i][j]=max(1ll,a[i][j]-x),r[i][j]=a[i][j]+x;
for(int i=1;i<=n;i++) s[i]=1;
for(int i=1;i<=m;i++) d[i]=2e9;
bool op=0;
while(1)
{
bool flag=0;
for(int i=1;i<=n;i++)
{
if(i-1&&s[i]<=s[i-1]) flag=1,s[i]=s[i-1]+1;
for(int j=1;j<=m;j++)
if(s[i]*d[j]<l[i][j]) flag=1,s[i]=(l[i][j]+d[j]-1)/d[j];
}
for(int i=m;i>=1;i--)
{
if(i+1<=m&&d[i]>=d[i+1]) flag=1,d[i]=d[i+1]-1;
for(int j=1;j<=n;j++)
if(s[j]*d[i]>r[j][i]) flag=1,d[i]=r[j][i]/s[j];
}
if(s[n]>d[m]||d[1]<=0) op=1;
if(op||!flag) break;
}
if(!op) return 1;
op=0;
for(int i=1;i<=n;i++) s[i]=2e9;
for(int i=1;i<=m;i++) d[i]=1;
while(1)
{
bool flag=0;
for(int i=1;i<=m;i++)
{
if(i-1&&d[i]<=d[i-1]) flag=1,d[i]=d[i-1]+1;
for(int j=1;j<=n;j++)
if(d[i]*s[j]<l[j][i]) flag=1,d[i]=(l[j][i]+s[j]-1)/s[j];
}
for(int i=n;i>=1;i--)
{
if(i+1<=n&&s[i]>=s[i+1]) flag=1,s[i]=s[i+1]-1;
for(int j=1;j<=m;j++)
if(s[i]*d[j]>r[i][j]) flag=1,s[i]=r[i][j]/d[j];
}
if(d[m]>s[n]||s[1]<=0) op=1;
if(op||!flag) break;
}
if(!op) return 1;
return 0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>a[i][j];
long long l=0,r=2000000000,ans=-1;
while(l<=r)
{
long long mid=(l+r)>>1ll;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
cout<<ans<<"\n";
check(ans);
for(int i=1;i<=n;i++) cout<<s[i]<<" ";
cout<<"\n";
for(int i=1;i<=m;i++) cout<<d[i]<<" ";
return 0;
}