Codeforces Round #828 (Div. 3)练习笔记
\(\text{A.Number Replacement}\)
给定长度为 \(n(n\leq 50)\) 的整数数组 \(a(a_i\leq 50)\) 和一个长度为 \(n\) 的字符串 \(s\) 。
定义一次操作为:选定整数 \(x\) 和字符 \(c\) ,将数组所有等于 \(x\) 的位置变成 \(c\) 。问是否能通过多次操作将 \(a\) 变为 \(s\) 。 \(T(T\leq 1000)\) 组数据。
\(\text{sol.}\) 注意到 \(a_i\) 很小,所以可以从左往右扫一遍,记录 \(id_{a_i}=s_i\) 。若当前 \(id_{a_i}\) 已经有值了且值不等于 \(s_i\) ,则无法将 \(a\) 变为 \(s\) 。
cint maxn = 60;
int T, n, a[maxn], id[maxn], fl;
char s[maxn];
int main(){
T = rd();
while(T--){
n = rd(), fl = 0;
fp(i, 1, n)a[i] = rd(), id[a[i]] = -1;
scanf("%s", s+1);
fp(i, 1, n){
if(~id[a[i]] && id[a[i]] != s[i]-'a'){ fl = 1; break; }
id[a[i]] = s[i]-'a';
}
puts(fl ? "NO" : "YES");
}
return 0;
}
\(\text{B. Even-Odd Increments}\)
给定长度为 \(n(n\leq 10^5)\) 的数组 \(a(a_i\leq 10^9)\) ,接下来有 \(q(q\leq 10^5)\) 次操作,每次操作将数组里的所有奇数或者所有偶数都加上某个值 \(([1,10^4])\) 。要求在每次操作后输出整个数组的和。多组数据。
直接记录一下现在数组里有多少个奇数、多少个偶数,每次操作的时候答案的改变值就可以直接计算出来。如果加的数是奇数,则需要修改一下奇数和偶数的个数。
cint maxn = 100010;
int T, n, q, a[maxn], cnt[2];
LL ans;
int main(){
T = rd();
while(T--){
n = rd(), q = rd(), ans = cnt[0] = cnt[1] = 0;
fp(i, 1, n)a[i] = rd(), ++cnt[a[i]&1], ans += a[i];
while(q--){
rg int typ = rd(), x = rd();
ans += 1ll*cnt[typ]*x;
if(x&1)cnt[!typ] += cnt[typ], cnt[typ] = 0;
printf("%lld\n", ans);
}
}
return 0;
}
\(\text{C. Traffic Light}\)
给定一个长度为 \(n(n\leq 2\times 10^5)\) 的只包含 \(\text{r,y,g}\) 的环形字符串,再给定一个 \(\text{r,y,g}\) 中的字符 \(c\) 。定义 \(f_i\) 表示第 \(i\) 个字符后面最近的 \(\text g\) 离 \(i\) 的距离。对于所有字符 \(c\) ,求出 \(f\) 的最大值。多组数据。
按照翻译后的题意做就行。处理环形字符串可以直接复制一遍接在后面。
cint maxn = 200010;
int T, n, nxt[maxn<<1], pos, ans;
char now, s[maxn<<1];
int main(){
scanf("%d", &T);
while(T--){
scanf("%d %c", &n, &now), scanf("%s", s+1), pos = n<<1|1, ans = 0;
fp(i, 1, n)s[n+i] = s[i];
fb(i, n<<1, 1){
if(s[i] == 'g')pos = i;
nxt[i] = pos;
}
fp(i, 1, n)if(s[i] == now && nxt[i]-i > ans)ans = nxt[i]-i;
printf("%d\n", ans);
}
return 0;
}
\(\text{D. Divisibility by 2^n}\)
给定一个长度为 \(n(n\leq 2\times 10^5)\) 的数组 \(a(a_i\leq 10^9)\) 。定义一次操作为将某个 \(a_i\) 变成 \(a_i\times i\) 。每个 \(i\) 只能被操作 \(1\) 次。求最少需要多少次操作,使得 \(2^n | \prod_{i=1}^n a_i\) 。多组数据。
首先求出 \(\prod_{i=1}^n a_i\) 中有多少个 \(2\) 相乘,假设有 \(m\) 个。然后将 \(1\)~\(n\) 中每个数的 \(2\) 次幂分离出来,并从大到小排序。按照 \(2\) 次幂的大小从大到小依次加入每个数的 \(2\) 次幂,一旦加起来大于等于 \(n-m\) 次就停止。此时加了多少次就是最少的操作次数。
cint maxn = 200010;
int T, n, a[maxn], f[maxn], sum, cnt;
int main(){
T = rd();
while(T--){
n = rd(), sum = cnt = 0;
fp(i, 1, n){
a[i] = rd();
rg int x = a[i];
while(x && x%2 == 0)x >>= 1, ++sum;
}
fp(i, 1, n){
f[i] = 0;
rg int x = i;
while(x && x%2 == 0)x >>= 1, ++f[i];
}
sort(f+1, f+1+n), reverse(f+1, f+1+n);
while(cnt < n && sum < n)sum += f[++cnt];
if(sum < n)puts("-1");
else printf("%d\n", cnt);
}
return 0;
}
\(\text{E. Divisible Numbers}\)
给定 \(4\) 个数 \(a,b,c,d(1\leq a<c\leq 10^9,1\leq b<d\leq 10^9)\) 。求出 \(x,y\) ,满足 \(a<x\leq c,b<y\leq d\) 并且 \(ab|xy\) 。 \(T(T\leq 10)\) 组数据。
\(x,y\) 都可以写成 \(a\) 的因数 \(\times\) \(b\) 的因数 \(\times\) 一个系数的形式。注意到一个数的因数个数很少,所以可以枚举一个 \(a\) 的因数 \(f_1\) ,再枚举一个 \(b\) 的因数 \(f_2\) ,那么 \(x=k_1f_1f_2,y=k_2\times \frac{ab}{f_1f_2}\) 。只需要求出 \(k_1,k2\) 使得 \(x,y\) 在题目要求的范围即可。
cint maxn = 100010;
int T, a, b, c, d, f[2][maxn], cnt[2], fl;
int main(){
T = rd();
while(T--){
a = rd(), b = rd(), c = rd(), d = rd(), fl = cnt[0] = cnt[1] = 0;
for(rg int i = 1; i*i <= a; ++i)if(a%i == 0){
f[0][++cnt[0]] = i;
if(i*i != a)f[0][++cnt[0]] = a/i;
}
for(rg int i = 1; i*i <= b; ++i)if(b%i == 0){
f[1][++cnt[1]] = i;
if(i*i != b)f[1][++cnt[1]] = b/i;
}
fp(i, 1, cnt[0])if(!fl)fp(j, 1, cnt[1]){
rg LL x = 1ll*f[0][i]*f[1][j];
rg LL y = 1ll*a*b/x;
x = a/x*x+x, y = b/y*y+y;
if(a < x && x <= c && b < y && y <= d){ printf("%lld %lld\n", x, y), fl = 1; break; }
}
if(!fl)puts("-1 -1");
}
return 0;
}
\(\text{F. MEX vs MED}\)
给定一个长度为 \(n(n\leq 2\times 10^5)\) 的由 \(0\)~\(n-1\) 的排列 \(p\) 。
定义 \(\text{mex}\) 为数列中最小的未出现的非负整数。
定义 \(\text{med}\) 为数列的中位数(若长度为偶数,则取第 \(\frac{len}{2}\) 小的数)。
求出有多少个区间满足 \(\text{mex} > \text{med}\) 。多组数据。
假设一个区间的 \(\text{mex}=m\) ,那么这个区间中就得出现 \([0,m-1]\) 中所有的数。由此可以得出,区间长度不能超过 \(2m\) ,否则 \(\text{med}\) 将大于 \(m\) 。
所以我们可以枚举 \(m\) ,维护最小的包含了 \([0,m-1]\) 所有数的区间。接下来考虑拓展这个区间。必然是某一个方向拓展到 \(m\) 所在位置,另一个方向拓展到数组尽头,所以直接计算有多少个满足条件的区间即可。
计算时可以直接枚举 \(m\) 所在的那一边的长度,这样可以保证复杂度是线性的。
cint maxn = 200010;
int T, n, p[maxn], pos[maxn], l, r, mex, rst;
LL ans;
int main(){
T = rd();
while(T--){
n = rd(), ans = 0;
fp(i, 1, n)p[i] = rd(), pos[p[i]] = i;
l = n+1, r = 0, mex = 0;
fp(i, 0, n-1){
if(l <= pos[i] && pos[i] <= r)continue;
if(pos[i] < l)l = pos[i];
if(pos[i] > r)r = pos[i];
while(mex < n && l <= pos[mex] && pos[mex] <= r)++mex;
if(mex == n)break;
rst = mex*2-(r-l+1);
if(pos[mex] < l)fp(i, pos[mex]+1, l)ans += max(0, min(n-r+1, rst-(l-i)+1));
else fp(i, r, pos[mex]-1)ans += max(0, min(l, rst-(i-r)+1));
}
printf("%lld\n", ans+1);
}
return 0;
}