「比赛题解」Educational Codeforces Round 96 题解(A~E)
B题被降智导致 30min 才过还被罚了五次时。后面总算找回状态,打的还不错。
A. Number of Apartments
Translate
\(T\) 次询问,对于每一次询问:
给定 \(n\) ,求 \(3x+5y+7z=n\) 的一个非负整数解。
Solution
有 \(5\bmod 3=2,7\bmod3=1\)
首先 \(n\) 是 \(3\) 的倍数,则 \(x=n/3,y=z=0\) 即可。
当 \(n\bmod 3=1\) 时,可以去掉两个 \(3\) 换成一个 \(7\) ,则 \(x=n/3-2,y=0,z=1\)
当 \(n\bmod 3=2\) 时,可以去掉一个 \(3\) 换成一个 \(5\),则 \(x=n/3-1,y=1,z=0\)
显然的,有 \(n=1,2,4\) 无解,因为不能构造出一组解。
Code
const int N = 1e6 + 10;
int n, a[N];
signed main() {
int T = read();
while(T--) {
n = read();
if(n <= 2 || n == 4) {
puts("-1");
continue;
}
int x = n / 3, y = 0, z = 0;
if(n % 3 == 1) x -= 2, z++;
if(n % 3 == 2) {
x -= 1;
y++;
}
printf("%d %d %d\n", x, y, z);
}
return 0;
}
B. Barrels
Translate
\(T\) 次询问,对于每一次询问:
有 \(n\) 个水桶,第 \(i\) 个水桶有 \(a_i\) 单位的水,有 \(k\) 次机会把一个桶里的水倒在另一个桶里(任意单位,可以不执行完),询问最后最大水桶水量和最小水桶水量的差的最大值。
\(1\leq k < n\leq 2\cdot10^5,0\leq a_i \leq10^9\)
Solution
贪心:差值最大,则让最大数尽量大,最小数尽量小。用 \(k\) 次机会把前 \((k+1)\) 大的水桶倒在一起,必剩有空桶(对应就是最小值为 \(0\) ),则差的最大值就是前 \((k+1)\) 大的数的和。
Code
#define ll long long
const int N = 1e6 + 10;
int n, k;
ll a[N], sum, maxx, minn, b[N];
signed main() {
int T = read();
while(T--) {
n = read();
k = read();
for(int i = 1; i <= n; ++i) a[i] = read();
std::sort(a + 1, a + n + 1);
sum = 0;
++k;
for(int i = n; i >= 1; --i) {
sum += a[i];
--k;
if(!k) break;
}
printf("%lld\n", sum);
}
return 0;
}
C. Numbers on Whiteboard
Translate
\(T\) 次询问,对于每一次询问:
给定 \(n\) 个数,为 \(1,2,3,...,n\),每次可以选择两个数 \(a,b\) 并删除,然后把 \(\left\lceil\frac{a+b}{2}\right\rceil\) 加入到这些数中。最小化最后剩下的一个数,输出这个数并且输出构造方案。
\(2\leq n\leq 2\cdot10^5,\sum n \leq 2\cdot10^5\)
Solution
对于 \(n=2\),剩下的最小数为 \(2\),方案为合并 \(1,2\)。
对于 \(n\geq 3\),我也不知道该怎么想出这个方法反正就是写写画画构造出这样一个方案:
把 \(n\) 和 \(n-1\) 合并成 \(n\),留下 \(n\)。
然后就有规律了:
\(n\) 和 \(n-2\) 合并,留下 \(n-1\)。
\(n-1\) 和 \(n-3\) 合并,留下 \(n-2\)。
\(n-2\) 和 \(n-4\) 合并,留下 \(n-3\)
\(...\)
最后只剩下 \(1\) 和 \(3\) ,合并得到 \(2\)。故最后得到 \(2\)。
证明最小的剩下的数就是 \(2\):反证法,因为得到 \(1\) 必须有两个 \(1\) ,而最多只会有一个 \(1\),故剩下的数最小为 \(2\)。
Code
int n, k;
int a[N], cnt[N];
signed main() {
int T = read();
while(T--) {
n = read();
if(n == 2) {
printf("2\n 1 2\n");
continue;
}
printf("%d\n%d %d\n", 2, n, n - 1);
int now = n;
for(int i = 2; i < n; ++i) {
printf("%d %d\n", now, now - 2);
now = now - 1;
}
}
return 0;
}
D. String Deletion
Translate
\(T\) 次询问,对于每一次询问:
给定一个长度为 \(n\) 的 \(01\) 串,每次操作可以选择一个位置 \(i\),消除第 \(i\) 位,然后把 \(01\) 串前面连续的一串 \(1\) 或者是一串 \(0\) 消除,求最大操作数。
Solution
先把一个个连续的串的长度存下来。
肯定不希望消掉一个位置之后会有串合并,而这种合并的情况就是消单个的数的情况。
所以说对于前面的单个的数,利用后面的多数删掉,如果前面的就是多数,直接删掉即可。直到不能删为止,剩下的肯定是交替的单数,这个时候只能一个一个消,因为不管怎样花式消答案都是一样的。
具体来讲,对于 \(1110100010\):
消 \(i=1\),得到 \(0100010\)。
消 \(i=3\),得到 \(10010\)。
消 \(i=2\),得到 \(010\)。
这个时候剩下的 \(010\),一个一个消可以消 \(2\) 次。
Code
我的代码实现比较难理解,建议自己写写吧。
const int N = 1e6 + 10;
int n, k;
int a[N], ant;
signed main() {
int T = read();
while(T--) {
n = read();
ant = 0;
int last = -1, now, cnt = 0;
for(int i = 1; i <= n; ++i) {
scanf("%1d", &now);
if(last != now) {
if(i != 1) a[++ant] = cnt;
last = now;
cnt = 1;
} else ++cnt;
}
a[++ant] = cnt;
int ans = 0, ans2 = 0;
int l = 1;
for(int i = 1; i <= ant; ++i) {
if(i < l) continue;
if(a[i] != 1) {
while(a[i] > 1 && l < i) {
if(a[l] == 0) {
++l;
continue;
}
a[i]--;
a[l] = 0;
++ans;
++l;
}
if(l == i) if(a[i] != 1) a[i] = 0, ++ans;
}
}
for(int i = 1; i <= ant; ++i) if(a[i] == 1) ++ans2;
printf("%d\n", ans + (ans2 + 1) / 2);
}
return 0;
}
E. String Reversal
Translate
给定长度为 \(n\) 的字符串,每次可以交换两个相邻位置的字符,求这个字符串到它原串反转后的串的最小交换次数。
\(n\leq 2\cdot 10^5\)
Solution
最小交换次数,交换相邻的...想到逆序对。
有能力的读者就可以停下来了,仔细想想发现这是个非常常见的套路。
把原串反转后的串的第一个字符到最后一个字符依次给予一个递增的权值,然后再传递给原串相应的位置那个权值,发现就是这道题。
那原串的反串的各个位的权值可以 \(1\) 到 \(n\),这个已经确定了,怎么传递给原串对应字符呢?相同的字符传递给谁更优呢?
因为求的是逆序对个数,所以原串相同字符的权值也递增会使答案更小。
这样就比较简单了,处理一下每个字符的权值然后逆序对即可。
Code
采用树状数组求解。
#define ll long long
const int N = 1e6 + 10;
int n, a[N], cnt[N], go[N];
char ch1[N], ch2[N];
ll tree[N << 1], ans;
inline int lowbit(int x) { return x & (-x); }
ll sum(int x) { ll rsum = 0; for(; x; x -= lowbit(x)) rsum += tree[x]; return rsum; }
void modify(int x, ll k) { for(; x <= n; x += lowbit(x)) tree[x] += k; }
std::vector<int>vec[27];
signed main() {
n = read();
for(int i = 1; i <= n; ++i) {
char now;
std:: cin >> now;
ch1[i] = now; ch2[n - i + 1] = now;
}
for(int i = 1; i <= n; ++i) {
vec[ch2[i] - 'a'].push_back(i);
++go[ch2[i] - 'a'];
}
for(int i = 1; i <= n; ++i) {
int now = ch1[i] - 'a';
a[i] = vec[now][cnt[now]++];
}
for(int i = 1; i <= n; ++i) {
modify(a[i], 1ll);
ans += i - sum(a[i]);
}
printf("%lld\n", ans);
return 0;
}
FG
不会