Educational Codeforces Round 92
A. LCM Problem
水题,显然 2 × a ≤ l c m ( a , b ) , b ≤ l c m ( a , b ) 2\times a\le \mathrm{lcm}(a,b) \; , \;b\le \mathrm{lcm}(a,b) 2×a≤lcm(a,b),b≤lcm(a,b),那么就做完了。
B. Array Walk
很明显是个DP,但是我调了好久才对,还是太菜了。
推导过程如下:
首先对于
z
=
0
z=0
z=0 的情况,显然最终结果是
d
p
0
=
m
a
x
0
=
∑
i
=
1
k
+
1
a
i
dp_0=max_0=\sum_{i=1}^{k+1}a_i
dp0=max0=∑i=1k+1ai
所以可以建一个前缀和数组。
z
=
1
z=1
z=1的时候除了不仅可以像上面那样走,还可以选择在路程中间后退一次或者在末端后退一次。在中间后退使得路径长度减少2,中间有连续的两个块会被经过两次;在末端后退会路径长度减少1,路径的倒数第二个块经过两次。于是可以再开一个数组记录到当前位置为止的最大相邻块值的和,即
m
[
i
]
=
max
(
m
[
i
−
1
]
,
a
[
i
−
1
]
+
a
[
i
]
)
m[i] = \max(m[i - 1], a[i - 1] + a[i])
m[i]=max(m[i−1],a[i−1]+a[i])
那么
m
a
x
1
=
max
(
p
r
e
[
k
−
1
]
+
m
[
k
−
1
]
,
p
r
e
[
k
]
+
a
[
k
−
1
]
)
max_1=\max(pre[k-1] + m[k-1], pre[k] + a[k-1])
max1=max(pre[k−1]+m[k−1],pre[k]+a[k−1])
显然,
d
p
1
=
max
(
d
p
0
,
m
a
x
1
)
dp_1=\max(dp_0,max_1)
dp1=max(dp0,max1)
z
≥
2
z\ge2
z≥2 的时候以此类推,可以得到
d
p
i
=
max
(
d
p
i
−
1
,
m
a
x
i
)
dp_i=\max(dp_{i-1},max_i)
dpi=max(dpi−1,maxi) 以及
m
a
x
i
=
max
(
p
r
e
[
k
+
1
−
2
×
i
]
+
i
×
m
[
k
+
1
−
2
×
i
]
,
p
r
e
[
k
+
2
−
2
×
i
]
+
(
i
−
1
)
×
m
[
k
+
2
−
2
×
i
]
+
a
[
k
+
1
−
2
×
i
]
)
max_i=\max(pre[k + 1 - 2 \times i] + i \times m[k + 1 - 2 \times i], pre[k + 2 - 2 \times i] + (i - 1) \times m[k + 2 - 2 \times i] + a[k + 1 - 2 \times i])
maxi=max(pre[k+1−2×i]+i×m[k+1−2×i],pre[k+2−2×i]+(i−1)×m[k+2−2×i]+a[k+1−2×i])
于是这题就做完了,复杂度 O ( k ) O(k) O(k)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 9;
inline int read()
{
int data = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
data = (data << 3) + (data << 1) + ch - '0';
ch = getchar();
}
return f * data;
}
int arr[maxn], pre[maxn], m[maxn];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("data.txt","w",stdout);
//std::ios::sync_with_stdio(false);
//std::cin.tie(0);
int T;
T = read();
while (T--)
{
memset(m, 0, sizeof(m));
int n = read(), k = read(), z = read();
for (int i = 1; i <= n; ++i)
{
arr[i] = read();
pre[i] = pre[i - 1] + arr[i];
m[i] = max(m[i - 1], arr[i - 1] + arr[i]);
}
int ans = pre[k + 1], tmp;
for (int i = 1; i <= z; ++i)
{
if (k < 2 * i - 1)
break;
tmp = max(pre[k + 1 - 2 * i] + i * m[k + 1 - 2 * i], pre[k + 2 - 2 * i] + (i - 1) * m[k + 2 - 2 * i] + arr[k + 1 - 2 * i]);
ans = max(ans, tmp);
}
cout << ans << '\n';
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
C. Good String
比赛的时候理解错了题意,试了二分和尺取法结果都过不了样例。第二天再看发现题目读错了。。。。。
显然,满足题目要求的左移和右移后相同的序列只可能是两种:
- 所有的元素全部相同
- 两个不同元素交替循环,如
0101010101
可以发现,如果序列长度是奇数那么必须是第一种
由于所有字符都是数字,可以把所有组合都枚举一遍计算好串长度最大值,再用总长度减一下就行了
这题到这里也就做完了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 9;
template <typename T>
inline void read(T &x)
{
T data = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
data = (data << 3) + (data << 1) + ch - '0';
ch = getchar();
}
x = f * data;
}
string s;
int calc(int x, int y)
{
int ans = 0;
for (int i = 0; i < s.length(); ++i)
if (s[i] - '0' == x)
{
ans++;
swap(x, y);
}
if (x != y && ans & 1)
ans--;
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("data.txt","w",stdout);
//std::ios::sync_with_stdio(false);
//std::cin.tie(0);
int T;
read(T);
while (T--)
{
cin >> s;
int ans = 0;
for (int i = 0; i <= 9; ++i)
for (int j = 0; j <= 9; ++j)
ans = max(ans, calc(i, j));
cout << s.length() - ans << '\n';
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
D. Segment Intersections
又是一个题面令人费解的题,并且比C难多了。
分类讨论很复杂,尤其两条线段不相交的情况。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &x)
{
T data = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
data = (data << 3) + (data << 1) + ch - '0';
ch = getchar();
}
x = f * data;
}
ll n, k, l1, l2, r1, r2;
int main()
{
int t;
read(t);
while (t--)
{
read(n), read(k), read(l1), read(r1), read(l2), read(r2);
ll ans = 1e18;
if (max(l1, l2) <= min(r1, r2))
{
ll rem = max((ll)0, k - n * (min(r1, r2) - max(l1, l2)));
ll mp = n * (abs(l1 - l2) + abs(r1 - r2));
ans = min(rem, mp) + max((ll)0, rem - mp) * 2;
}
else//不相交
{
ll inv = max(l1, l2) - min(r1, r2);//间隔
for (int i = 1; i <= n; ++i)
{
ll tmp = i * inv;//补上间隔
ll mp = (max(r1, r2) - min(l1, l2)) * i;//最大长度
tmp += min(k, mp) + max((ll)0, k - mp) * 2;//调整
ans = min(ans, tmp);
}
}
cout << ans << '\n';
}
}
照着题解的kotlin代码写了一份c++的
E. Calendar Ambiguity
由题,
x
×
d
+
y
≡
y
×
d
+
x
(
m
o
d
w
)
x\times d+y\equiv y\times d+x \pmod w
x×d+y≡y×d+x(modw),变形可得:
(
x
−
y
)
×
(
d
−
1
)
≡
0
(
m
o
d
w
)
(x-y)\times(d-1)\equiv 0\pmod w
(x−y)×(d−1)≡0(modw)
那么就很明显了,既然
d
d
d 和
w
w
w 都已知了,
x
−
y
x-y
x−y 一定是
w
gcd
(
w
,
d
−
1
)
\frac{w}{\gcd(w,d-1)}
gcd(w,d−1)w 的整数倍。接下来等差数列求和就可以解决了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &x)
{
T data = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
data = (data << 3) + (data << 1) + ch - '0';
ch = getchar();
}
x = f * data;
}
ll m, d, w;
int main()
{
int t;
read(t);
while (t--)
{
read(m), read(d), read(w);
ll w2 = w / __gcd(d - 1, w);
ll md = min(m, d);
ll cnt = md / w2;
ll ans = (2 * md - w2 * (cnt + 1)) * cnt / 2;
cout << ans << '\n';
}
}