A. Find The Array

题意

要你构造一个长度为$n$的数组,每个元素$a_i$满足:

  1. $a_i=1$
  2. $a_i-1$或者$a_i-2$存在于数组中

现在要你构造一个元素和为$s$的数组,问最短长度为多少

分析

贪心,让数组元素尽量大,但是因为$a_i-2$必须在数组中,所以构造的是$1,3,5,7,…$. 最后一个数如果溢出可以将其减小至和恰好为$s$,不影响性质。

代码

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
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;

signed main()
{
DDLC_ESCAPE_PLAN_FAILED;
int t;
cin >> t;
while(t--)
{
int sum = 0;
int n;
cin >> n;
for(int i = 1; ; i += 2){
sum += i;
if(sum >= n){
cout << (i + 1) / 2 << endl;
break;
}
}
}
return 0;
}

B. Maximum Cost Deletion

题意

给一个01串。每次操作可以选其中连续一段由相同字符构成的子串并将其删除。删除后剩下的部分保留顺序,拼接起来。每次删可以得到$a·l+b$分($l$为删的长度)。问删完后最大得分是多少

分析

考虑串中每个元素的平均贡献,即$\frac{a·l+b}{l}=a+\frac{b}{l}$. 由此可知不管怎么删,每个元素都一定会贡献$a$分。有变数的只有$b$. 当$b\geq 0$,应让$l$尽量小来最大化分数,所以$l=1$,答案是$总长度·(a+b)$;而若$b<0$,尽量让$l$变大。所以尽可能地扩展长度,每次都把整个连续的串删掉。

这个时候,把字符串分成$x$块,每块都是最长的连续的0或者1,这些块的邻居肯定是不同字符。每次删除之后,相邻字符会拼接起来。每次一个块一个块地删,最后总共删$x/2+1$次. 则答案为$a·l+(x/2+1)·b$

代码

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
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;

signed main()
{
DDLC_ESCAPE_PLAN_FAILED;
int t;
cin >> t;
while(t--)
{
int n, a, b;
string s;
cin >> n >> a >> b >> s;
if(b > 0){
cout << (int)s.size() * (a + b) << endl;
}
else{
char now = '.';
int cnt = 0;
for(int i = 0; i < s.size(); ++i){
if(s[i] != now){
now = s[i];
cnt++;
}
}
cout << (cnt / 2LL + 1LL) * b + a * (int)s.size() << endl;
}
}
return 0;
}

C. Manhattan Subarrays

题意

给数组$a$,则规定其对应了平面上$n$个点$(a_1,1),(a2,2),…(a_i,i)…$ 现在要求选出一个连续子数组,保证其长度小于3,或者从其中任选3个点$x,y,z$,不可能有$d(x,y)+d(y,z)=d(x,z)$。其中$d(x,y)$为$(a_x,x)$和$(a_y,y)$的曼哈顿距离](https://baike.baidu.com/item/曼哈顿距离/743092)),问可选出多少这样的子数组。

分析

由于各个点的$y$坐标都不一样,可以画图。随意在坐标图上画3个$y$坐标两两不同的点,改变它们$x$坐标之间的关系。可以发现,当按$y$坐标顺序看这三个点时,当且仅当它们的$x$坐标单调不下降或者单调不上升时,有$d(x,y)+d(y,z)=d(x,z)$。

那么对于长度为4的子数组,我们需要判断4次,每次从其中任选3个判断一下。

但对于长度5的数组,我们发现,不管怎么画,都必定会出现$d(x,y)+d(y,z)=d(x,z)$。

因此,满足条件的数组最长长度为4。而长度为2,1的必定符合条件,故我们只需判断长度为3,4的即可。

代码

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
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn];
bool check(int x, int y, int z)
{
if(a[x] <= a[y] && a[y] <= a[z]) return 0;
if(a[x] >= a[y] && a[y] >= a[z]) return 0;
return 1;
}
signed main()
{
DDLC_ESCAPE_PLAN_FAILED;
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
fors(i, 1, n) cin >> a[i];
int ans = 2 * n - 1;
for(int len = 3; len <= 4; ++len){
for(int l = 1; l + len - 1 <= n; ++l){
if(len == 3 && check(l, l + 1, l + 2)) ans++;
else if(len == 4 && (check(l, l + 1, l + 2) && check(l, l + 2, l + 3) && check(l + 1, l + 2, l + 3) && check(l, l + 1, l + 3))) ans++;
}
}
cout << ans << endl;
}
return 0;
}