[CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!)](https://codeforces.com/contest/1870)

(2023年9月18日晚)

C. Colorful Table

题意

给出n个数 ,以及一个数k,所有数满足 ,数组b是一个nxn的二维数组,其中 ,对于每一个数 ,求出面积最小的矩阵,使得矩阵中涵盖了b数组中的所有的,输出矩阵的两边之和(例如矩阵为3x3就输出6(6=3+3),矩阵为2x5就输出7(7=2+5))。

思路

例如a数组为(k = 4):

1 2 3 4 1

那么b数组为:

1 1 1 1 1
1 2 2 2 1
1 2 3 3 1
1 2 3 4 1
1 1 1 1 1

这样就可以得出我们应该输出

10,6,4,2

我们可以按照由大到小地填充b表格,每次填充对应的行和列,并且直接覆盖之前填充好的数据,最终就能得出b数组。

![CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!)](C:\Users\ACMLAB\AppData\Roaming\Typora\typora-user-images\CodeTON Round 6_1.png)

可以发现,大数会被更小的数”蚕食“
以3为例,起初3占满了所有行和列,但由于有更小的数1和2,在填充1和2时,1,2数字所在的行列(1,2,5)都被覆盖掉了。导致只剩下第3,4行存在3。因此面积最小应该为2x2 ,输出4。

由此推广出来,一个数会这些行留下自己的身影:, 因此对于任何一个数x,我们只需要找到 区间 使得对于任意的 都有 。 也就是 涵盖了所有的大于等于x的数,此时这个最小矩阵的边长就是undefinedr-l+1)2*(l-r+1)$ 。

特判:当一个数在a中不存在时,需要输出0。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
#include<vector>
using namespace std;
int main(){
int t ;
cin>>t;
while(t--){
int n,k;
scanf("%d %d",&n,&k);
vector<bool> isok(k+5);//记录某个数是否出现过,请注意这里的数组大小应该是k而不是n
vector<int> v(n+5);//原始数据
vector<int> qz(n+5);//前缀, qz[i]表示前i个数中的最大值
vector<int> hz(n+5);//后缀,hz[i]表示后i个数中的最大值
for(int i = 1;i<=n;i++){
scanf("%d",&v[i]);
isok[v[i]] = true;
}
qz[0] = hz[n+1] = 0;
for(int i = 1;i<=n;i++){
qz[i] = max(qz[i-1],v[i]);
}
for(int i = n;i>=1;i--){
hz[i] = max(hz[i+1],v[i]);
}
int l = 1,r = n;
for(int i = 1;i<=k;i++){
if(!isok[i]) {cout<<0<<" ";continue;}
while(qz[l]<i) l++;//跳过所有小于i的值
while(hz[r]<i) r--;
cout<<(r-l+1)*2<<" ";
}
puts("");
}
return 0;
}

D - Prefix Purchase

题意

给出一个n个数的数组a. 起初 ,再给出一个n个数的数组c, 现在你手上有k个金币,可以进行如下操作任意次:
从数组c中选择一个数 ,花费 个金币,使得数组a的前i项加一,即

问如何操作才能使得最终的数组a 的字典序尽可能大。

思路

首先我们知道,选择越靠后,他的“价值”就越高。因此如果存在 ,那么选择 就一定优于 ,因此我们可以将这种情况下的 的值改为 。在这种操作之后,我们会得到一个单调递增的c数组。 之后进行如下操作
首先尽可能地购买 ,此时可能手上剩余一些钱(设为left),由于越靠后”价值“越高,所以我们可以继续花费left来升级(升级指的就是 将一部分 变为 , 显而易见,我们应当尽可能地花费剩余的钱去升级。因此有如下递推式

最终得到的a数组就是我们所需要的a数组

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
#include<vector>
using namespace std;
void solve(){
int n;
scanf("%d",&n);
vector<int> c(n+5);//c数组
for(int i = 1;i<=n;i++){
scanf("%d",&c[i]);
}
int k;
scanf("%d",&k);//k
//对c数组进行预处理
int mn = 1e9;
for(int i = n;i>=1;i--){
mn = min(mn,c[i]);
c[i] = mn;
}
vector<int> a(n+5);//a数组
a[1] = k / c[1];
int left = k % c[1];
for(int i = 2;i<=n;i++){//递推
int cha = c[i] - c[i-1];
if(cha == 0){
a[i] = a[i-1];
continue;
}
a[i] = min(a[i-1],left/cha);
left -= cha * a[i];
}
for(int i = 1;i<=n;i++){
printf("%d%c",a[i]," \n"[i==n]);
}
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}