Codeforces Round #622 (Div.2)

比赛链接 / 官方题解链接

A. Fast Food Restaurant

Solution

最多最多就 $7$ 种搭配,$a,b,c,ab,ac,bc,abc$,从大到小依次去满足即可。

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<algorithm>
#include<cstdio>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

int T, a, b, c;

int main(){
read(T);
while(T--){
int ans = 0;
read(a, b, c);
if(a < b) swap(a, b); if(a < c) swap(a, c); if(b < c) swap(b, c);
if(a) a--, ans++; if(b) b--, ans++; if(c) c--, ans++;
if(a && b) a--, b--, ans++; if(a && c) a--, c--, ans++; if(b && c) b--, c--, ans++;
if(a && b && c) a--, b--, c--, ans++;
printf("%d\n", ans);
}
return 0;
}

B. Different Rules

卡了好久……好歹把式子弄出来了,差点以为要挂在 B 题上……

看到官方题解也是庞大的一坨,我就放心了(雾

Solution

不失一般性,假设 $x\leq y$. 下面的图中,第一列代表第一轮,第二列代表第二轮,每一行代表一个人的得分。

我们首先看一种大家“皆大欢喜”的情况——所有人总得分都一样,第一轮得了 $i$ 分,第二轮就得 $n+1-i$ 分(图1)。接下来分情况讨论:

  • $x+y\leq n$:此时图1中第二列的 $y$ 在第一列的 $x$ 下方(图2-1),已知条件是,第二列的这个 $y$ 往上跑到和第一列的 $x$ 同一层去了。
    • 最小排名:交换第二列的 $y$ 和与第一列的 $x$ 同一层的那个元素,显然就是第1名了(图2-2);
    • 最大排名:$y$ 往上跑了 $(n-y+1)-x$ 层导致了 $x+y$ 变小了,于是其他人很不爽,也想把第二轮的得分变小且变小到总分与 $x+y$ 相等就够了,于是大家都往上移 $(n-y+1)-x$ 层,总体上来看第二列往上“滚”(第 $i$ 层到第 $i-1$ 层,第 $1$ 层到最后一层)了 $(n-y+1)-x$ 层(图2-3)。从上面滚到下面去了的有 $(n-y+1)-x$ 个,这些分事实上变大了,不计入答案,所以最大排名是 $n-[(n-y+1)-x]=x+y-1$.
  • $x+y>n$:此时图1中第二列的 $y$ 在第一列的 $x$ 上方(图3-1),已知条件是,第二列的这个 $y$ 往下跑到和第一列的 $x$ 同一层去了。
    • 最大排名:交换第二列的 $y$ 和与第一列的 $x$ 同一层的那个元素,显然就是第 $n$ 名了(图3-2);
    • 最小排名:$y$ 往下跑了$x-(n-y+1)$ 层导致了 $x+y$ 变大了,其他人都很关心它,想把第二轮得分变大,于是大家都往下移 $x-(n-y+1)$ 层,总体上来看第二列往下“滚”了 $x-(n-y+1)$ 层,可惜还不够,为使 $x+y$ 排名更小,其他元素还得再往下移一层(图3-3)。从下面滚到上面去了的有 $\min\{n-1,x-(n-y+1)+1\}$ 个(小心 $x=y=n$ 的特殊情况),这些分事实上变小了,不计入答案,所以最小排名是 $\min\{n-1,x-(n-y+1)+1\}+1=\min\{n,x+y-n+1\}$.

把上面两种情况整合起来,最小排名 $=\max(\min(n,x+y+1-n),1)$,最大排名 $=\min(x+y-1,n)$.

1 2-1 2-2 2-3
3-1 3-2 3-3

Code

>folded
1
2
3
4
5
6
7
8
9
10
11
12
#include<algorithm>
#include<cstdio>
using namespace std;
int T, n, x, y;
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &x, &y);
printf("%d %d\n", max(min(n, x + y + 1 - n), 1), min(x + y - 1, n));
}
return 0;
}

C1. Skyscrapers (easy version)

Solution

依次枚举每一个元素作为最高点,然后向左右两边暴力扩展即可。

复杂度:$O(n^2)$

Code

>folded
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
#include<algorithm>
#include<cstdio>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;

const int N = 1005;

int n;
LL m[N], f[N], rec[N], ans[N];

int main(){
read(n);
for(int i = 1; i <= n; i++) read(m[i]);
for(int i = 1; i <= n; i++){
LL res = m[i];
rec[i] = m[i];
for(int j = i - 1; j >= 1; j--){
LL now = min(m[j], rec[j+1]);
res += now;
rec[j] = min(m[j], rec[j+1]);
}
for(int j = i + 1; j <= n; j++){
LL now = min(m[j], rec[j-1]);
res += now;
rec[j] = min(m[j], rec[j-1]);
}
if(ans[0] < res){
ans[0] = res;
for(int j = 1; j <= n; j++) ans[j] = rec[j];
}
}
for(int i = 1; i <= n; i++)
printf("%lld ", ans[i]);
return 0;
}

C2. Skyscrapers (hard version)

唉,比赛时在正解的边缘疯狂试探,然后逐渐偏离正解,用分治解出了最大值,突然想起题目要求的是方案,结果复杂度降不下来了……

Solution

预处理每一个元素左右最近的小于它的元素位置(设为 $L_i,R_i$),然后就可以递推了:

  • 设 $f_i$ 是以第 $i$ 个元素结尾的最大上升序列的值,那么 $f_i=f_{L_i}+m_i\cdot(i-L_i)$.
  • 设 $g_i$ 是以第 $i$ 个元素开头的最大下降序列的值,那么 $g_i=g_{R_i}+m_i\cdot(R_i-i)$.

然后枚举每一个点作为最大值,第 $i$ 个点作为最大值时的答案就是 $f_i+g_i-m_i$ 了。靠 $L$ 和 $R$ 数组也能够线性地把方案解出来。

怎么预处理呢?我的方法是 ST 表+二分查找(说实话有点奇怪……毕竟以前从来没有在 ST 表上二分过……)

复杂度:$O(n\lg n)$

Code

>folded
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<algorithm>
#include<cstdio>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;

const int N = 500005;

int n, L[N], R[N];
LL m[N], f[N], g[N], h[N], ans;

int lg[N];
LL rmq[N][20];
void pre(){
lg[1] = 0, lg[2] = 1;
for(int i = 3; i <= n; i++)
lg[i] = lg[i/2] + 1;
}
void init(){
for(int i = 1; i <= n; i++) rmq[i][0] = m[i];
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
rmq[i][j] = min(rmq[i][j-1], rmq[i+(1<<(j-1))][j-1]);
}
int query(int l, int r){
int k = lg[r - l + 1];
return min(rmq[l][k], rmq[r-(1<<k)+1][k]);
}

inline void find(int x){
int l = 0, r = x - 1;
while(l < r){
int mid = (l + r) >> 1;
if(query(mid + 1, r) < m[x]) l = mid + 1;
else r = mid;
}
L[x] = l;
l = x + 1, r = n + 1;
while(l < r){
int mid = (l + r) >> 1;
if(query(l, mid) < m[x]) r = mid;
else l = mid + 1;
}
R[x] = r;
}

int main(){
read(n);
for(int i = 1; i <= n; i++) read(m[i]);
pre(); init();
for(int i = 1; i <= n; i++) find(i);
for(int i = 1; i <= n; i++) f[i] = f[L[i]] + m[i] * (i - L[i]);
for(int i = n; i >= 1; i--) g[i] = g[R[i]] + m[i] * (R[i] - i);
int mark = 0;
for(int i = 1; i <= n; i++){
if(ans < f[i] + g[i] - m[i]){
ans = f[i] + g[i] - m[i];
mark = i;
}
}
// printf("%lld\n", ans);
int now = mark;
while(now){
for(int i = L[now] + 1; i <= now; i++) h[i] = m[now];
now = L[now];
}
now = mark;
while(now <= n){
for(int i = now; i <= R[now] - 1; i++) h[i] = m[now];
now = R[now];
}
for(int i = 1; i <= n; i++) printf("%lld ", h[i]);
return 0;
}

D. Happy New Year

留坑……

作者

xyfJASON

发布于

2020-02-23

更新于

2020-12-20

许可协议

评论