CF1870C 题解
似乎正解不是这样,我来讲一下我的做法。
题意:
给定序列 \(a\),构建矩阵 \(b\) 使得 \(b_{i,j}=\min(a_i,a_j)\)。求解对于值域里每一个数:在矩阵上的最小包含矩形的长宽之和。
分析:
例如:对于 \({1,4,2,3}\) 的矩阵如下

易于发现的性质:该矩阵关于主对角线对称(这是显然的)。另外在这张图上,覆盖 \(1,2,3,4\) 的矩形分别为边长为 \(4,3,3,1\) 的正方形。
考虑原因:每一个出现的数都一定对称的成对出现,分布在以主对角线为对称轴的正方形的两个顶点,因此最小覆盖矩形一定是正方形。
因此,如果一个数出现在了某一行或某一列的某些位置,对他的最小覆盖的正方形的边长就是对这些位置的最小覆盖(例如在上图中:\(3\) 占据了第二列和第四列,那么该正方形的边长就是3)。比较有趣的是,只有一个数完全占据了剩余矩阵的第一行或最后一行(列)下一个数的答案才会有所缩减。
由于图是对称的,我们不妨只对一维考虑。把序列 \(a\) 从小到大排序,最小的一定至少占据了一行或一列(既然他是最小,这一行一列的最小值就是他),他的答案就是剩余矩阵的边长。然后统计这一个数对答案的影响:如果删去了头尾的一行,他就会对剩余矩阵有所影响。
做完就行了,统计影响是 \(O(n)\) 的,复杂度瓶颈在排序上 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
int t, n, k, a[100010], b[100010], vis[100010];
inline bool cmp(int x, int y){return a[x] < a[y];}
int main(){ memset(vis, -1, sizeof vis);
cin >> t; while(t--){
cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) b[i] = i; int l = 1, r = n;
sort(b + 1, b + n + 1, cmp); int res = n, lasans = 0;
for(int i = 1; i <= n;){
for(int j = lasans + 1; j < a[b[i]]; j++) cout << 0 << ' ';
cout << res * 2 << ' '; int p = i; vis[b[p]] = t;
while(a[b[p]] == a[b[i]]) vis[b[p++]] = t; lasans = a[b[i]];
for(l = l; l <= r;){if(vis[l] != t) break; l++, res--;}
for(r = r; r >= l;){if(vis[r] != t) break; r--, res--;} i = p;
} for(int i = lasans + 1; i <= k; i++) cout << 0 << ' '; cout << '\n';
}
}

浙公网安备 33010602011771号