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';
    }
}
posted @ 2023-10-01 01:09  xlpg0713  阅读(28)  评论(0)    收藏  举报