Contest传送门
A.String Building
题意
给定 4 4 4个初始字符串{ a a , a a a , b b , b b b aa,aaa,bb,bbb aa,aaa,bb,bbb},问能否用其中字符串连接成询问的字新字符串(不是每个都要用到)
思路
只要新字符串中相同字母都是连续出现的就是可行的。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;
void solve() {
string s;
cin >> s;
if (s.size() == 1) {
cout << "NO" << endl;
return;
}
for (int i = 1; i < s.size() - 1; i++) {
if (s[i] != s[i - 1] && s[i] != s[i + 1]) {
cout << "NO" << endl;
return;
}
}
if (s[0] != s[1] || s[s.size() - 2] != s[s.size() - 1]) {
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}
B.Consecutive Points Segment
题意
给定
x
x
x轴上的
n
n
n个点,要求把这些点移到一起形成一段连续区间(点之间不能重叠),操作:对每个
x
i
x_i
xi都可以进行最多一次位移,
+
1
+1
+1或者
−
1
-1
−1。
(
1
≤
n
≤
2
∗
(1≤n≤2*
(1≤n≤2∗
1
0
5
10^5
105
)
)
)
,
,
,
(
∑
n
≤
2
⋅
(∑n≤2⋅
(∑n≤2⋅
1
0
5
10^5
105
)
)
)
,
,
,
(
1
≤
x
i
≤
(1≤x_i≤
(1≤xi≤
1
0
6
10^6
106
)
)
)。
思路
从左端模拟到右端,同时记录前面遍历过的点有没有进行过位移操作,如果相邻两个数不满足条件就可以直接输出 N O NO NO了。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;
int n, a[N], st[N];
// st数组记录第i个数有没有位移过
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) st[i] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
bool ff = false; //记录当前位置左边的数是不是都未位移过
for (int i = 1; i < n; i++) {
if (a[i + 1] - a[i] > 3) {
cout << "NO" << endl;
return;
} else if (a[i + 1] - a[i] == 3) { //应该第i个右移,第i+1个左移
if (ff) { //如果左边的数有一个及以上位移过了,那当前操作后就会有空隙
cout << "NO" << endl;
return;
}
a[i + 1]--;
a[i]++;
st[i] = 1;
st[i + 1] = 1;
ff = true;
} else if (a[i + 1] - a[i] == 2) {
if (ff) { //优先移左边,左边操作了就移右边
a[i + 1]--;
st[i + 1] = 1;
}
ff = true;
}
}
cout << "YES" << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}
C. Dolce Vita
题意
给定
n
n
n 种商品的原始价格
a
i
a_i
ai,每种商品每天都会贵
1
1
1 元 , 每天有
x
x
x元去购买商品(每种商品每天最多买一个,钱不会结余),问到最后(即一个都买不起)时最多能买几个商品。
(
1
≤
n
≤
2
⋅
(1≤n≤2⋅
(1≤n≤2⋅
1
0
5
10^5
105
;
1
≤
x
≤
; 1≤x≤
;1≤x≤
1
0
9
10^9
109
)
)
)
,
,
,
(
∑
n
≤
2
⋅
(∑n≤2⋅
(∑n≤2⋅
1
0
5
10^5
105
)
)
)
,
,
,
(
1
≤
a
i
≤
(1≤a_i≤
(1≤ai≤
1
0
9
10^9
109
)
)
)。
思路
首先贪心很好想到,每次都买前几个便宜的商品,所以先对
a
i
a_i
ai排好序后处理前缀和 ,然后再想办法实现一下这个价格上涨的过程就好了。
每种商品每天贵
1
1
1元,其实就是每天拿到的钱会扣
n
n
n元,所以只要改变
x
x
x的值,在前缀和数组里二分找当前天能买的个数,累加一下就可以了。
但由于
x
x
x和
n
n
n的值可以差很多,所以还需要对模拟优化一下,假如
x
x
x很大,
a
i
a_i
ai 又都很小,那其实在一段日子里能买的个数是固定的,所以可以直接跳过这些日子,这样就相当于每次模拟能买的个数是严格递减的。
(写的很丑 ,没开long long TLE了好几次)
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;
LL n, x, a[N];
void solve() {
cin >> n >> x;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++) {
a[i] += a[i - 1];
}
LL ans = 0;
int l = 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] <= x)
l = mid;
else
r = mid - 1;
}
if (a[l] > x) {
cout << 0 << endl;
return;
}
ans += l;
x -= n;
int cnt = 1; //记录第几天
for (;;) {
int l = 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] <= x + (n - mid) * cnt) //每天扣掉n元后,购买前i个商品时,还要补上多扣的n - i元
l = mid;
else
r = mid - 1;
}
if (a[l] > x + (n - l) * cnt) break;
int ti = (x + (n - l) * cnt - a[l]) / l + 1; //跳过购买结果一样的日子
ans += ti * l;
cnt += ti;
x -= ti * n;
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}
D. Insert a Progression
题意
给定
n
n
n ,
x
x
x ,要求把
1
,
2
,
.
.
.
.
.
,
n
1 , 2 , ..... , n
1,2,.....,n 插入到长度为
n
n
n的
a
a
a数组中,使得插入后相邻两位之差的绝对值的和最小。
(
1
≤
n
,
x
≤
2
⋅
(1≤n,x≤2⋅
(1≤n,x≤2⋅
1
0
5
10^5
105
)
)
)
,
,
,
(
∑
n
≤
2
⋅
(∑n≤2⋅
(∑n≤2⋅
1
0
5
10^5
105
)
)
)
,
,
,
(
1
≤
a
i
≤
2
⋅
(1≤a_i≤2⋅
(1≤ai≤2⋅
1
0
5
10^5
105
)
)
)。
思路
我们可以惊奇的发现
1
1
1到
x
x
x这几个数中中只有大于原数组中最大值和小于原数组中最小值的才会对结果产生影响 , 其他数只要找能形成单调顺序的位子插入就可以,并且结果不会变化。
比如原数组是{
2
,
7
2,7
2,7} , 插入
1
1
1到
8
8
8 , 那么
2
2
2到
7
7
7直接按顺序插入到中间就好了,此时和还是
5
5
5。
那么剩下的数该怎么做呢。就看小于数组最小值的部分,设当前数为
n
u
m
num
num,可以发现有两种情况:接到左端或右端,或者插入到中间两个数
(
(
(假设左小右大
)
)
)之间。插入到中间的贡献是
(
a
i
+
1
−
n
u
m
)
+
(
a
i
−
n
u
m
)
−
(
a
i
+
1
−
a
i
)
=
(a_{i+1} - num) +(a_i - num) - (a_{i+1}- a_i) =
(ai+1−num)+(ai−num)−(ai+1−ai)=
2
∗
(
2*(
2∗(
a
i
a_i
ai -
n
u
m
)
num)
num) , 为了使最后的结果尽量小,直接找
a
i
=
M
i
n
a_i = Min
ai=Min 插入就可以了。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long LL;
const int N = 2e5 + 7;
const int mod = 1e9 + 7;
int n, x, a[N];
void solve() {
cin >> n >> x;
vector<int> a(n);
LL tot = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (i) tot += abs(a[i] - a[i - 1]);
}
int Max = max_element(a.begin(), a.end()) - a.begin();
int Min = min_element(a.begin(), a.end()) - a.begin();
LL ans = tot;
if (a[Min] > 1) {
if (a[0] == a[Min] || a[n - 1] == a[Min]) {
ans += a[Min] - 1;
} else {
ans += min(min(a[0], a[n - 1]) - 1, 2 * (a[Min] - 1));
}
}
if (a[Max] < x) {
if (a[0] == a[Max] || a[n - 1] == a[Max]) {
ans += x - a[Max];
} else {
ans += min(x - max(a[0], a[n - 1]), 2 * (x - a[Max]));
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}
累死了