[Problem?Discription]\color{blue}{\texttt{[Problem Discription]}}[Problem?Discription]
給定一個 n×mn \times mn×m 的表格 ai,ja_{i,j}ai,j?,你可以恰好進行一次如下操作:
- 選擇一個格點 (r,c)(r,c)(r,c)。
- 對于所有滿足 i=ri=ri=r 或者 j=cj=cj=c 的格點 (i,j)(i,j)(i,j),使 ai,j←ai,j?1a_{i,j} \leftarrow a_{i,j}-1ai,j?←ai,j??1。
求操作后所有格點最大值最小可能為多少。
多組數據,滿足 1≤n×m≤1×1051 \leq n \times m \leq 1 \times 10^{5}1≤n×m≤1×105,且所有數據的 n×mn \times mn×m 的總和不超過 2×1052 \times 10^{5}2×105。
[Analysis]\color{blue}{\texttt{[Analysis]}}[Analysis]
記原來矩陣的最大值為 ttt,顯然由于只能進行一次操作,且一次操作最多讓一個格點的值減小 111,所以最終答案要么是 ttt,要么是 (t?1)(t-1)(t?1)。
思考哪些情況會讓答案為 (t?1)(t-1)(t?1)。
顯然,當所有取得最大值的格點要么分布在第 rrr 行要么分布在第 ccc 列時,我們可以通過一次操作 (r,c)(r,c)(r,c) 把所有最大值都干掉;否則答案為 ttt 不變。
統計每一行每一列有多少個最大值,分別記為 cntri,cntcj\text{cntr}_{i},\text{cntc}_{j}cntri?,cntcj?,顯然第 iii 行和第 jjj 列的最大值的個數即為:
f(i,j)=cntri+cntcj?[ai,j=max?1≤i≤n,1≤j≤m{ai,j}]f(i,j)=\text{cntr}_{i}+\text{cntc}_{j}- \left [a_{i,j}=\max\limits_{1 \leq i \leq n, 1 \leq j \leq m} \{a_{i,j} \}\right ]f(i,j)=cntri?+cntcj??[ai,j?=1≤i≤n,1≤j≤mmax?{ai,j?}]
顯然預先統計出最大值的個數 cnt\text{cnt}cnt,如果某個格點 (i,j)(i,j)(i,j) 滿足 f(i,j)=cntf(i,j)=\text{cnt}f(i,j)=cnt,那么這個 (i,j)(i,j)(i,j) 就是我們需要的格點。
Code\color{blue}{\text{Code}}Code
const int N=1e5+100;int OneZDoubleY(){int n=read(),m=read();vector<int> a[n+2];for(int i=1;i<=n;i++){a[i].push_back(0);for(int j=1;j<=m;j++)a[i].push_back(read());}int maxn=a[1][1];for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)ckmax(maxn,a[i][j]);int cnt=0;vector<int> cntr(n+1),cntc(m+1);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if (a[i][j]==maxn){++cntr[i];++cntc[j];++cnt;}bool flag=false;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int t=cntr[i]+cntc[j];if (a[i][j]==maxn) t--;if (t==cnt){flag=true;break;}}return printf("%d\n",flag?maxn-1:maxn);
}
//大家可以挑戰一下不用 vector,用 malloc 來處理int main(){int T=read();while (T--) OneZDoubleY();return 0;
}