Codeforces Round 576 (Div. 2)
真的是好久没打\(CF\)了,不过这次做出来四道题(一共六道),虽然第三题\(FST\)了,不过还是涨了好多\(rating\)
之前有两次发挥不佳,掉到了\(1286\)分,这次直接涨了\(143\)分,重回青名
--
A.City Day
日常送分题,模拟就好了
B.Water Lily
初中数学题,用勾股定理列方程就能解出公式
C.MP3
稍微翻译下:
有\(n\)个数
定义一种操作:指定两个数\(l,r \; (l < r)\),将所有\(<l\)的数变为\(l\),所有\(>r\)的数变为\(r\)
只进行一次这种操作,使数的种类小于\(k\)种且此操作更改的数尽量少\((k = 2 ^ {(I/n)})\)
输入\(n,I\)和这\(n\)个数
输出最少要更改多少个数字
题解
先离散化,然后记录下每种数的数量,接下来问题就变成了求长度\(<=k\)的最大子区间,可以用尺取法,不过我比较懒,因为数都是正数,直接用的前缀和枚举
要特别注意:\(k\)可能会非常大,甚至爆\(LL\),但数最多只有\(400000\)种,所以\(k\)其实最大设为\(400000\)就好了(我就是因为这个\(FST\)了)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std;
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
int a[400010], b[400010];
int tong[400010], sum[400010];
int main() {
int n = read(), I = read() * 8;
for(int i = 1; i <= n; ++i) a[i] = b[i] = read();
int k;
if(I/n < 31)
k = 1 << (I/n);
else k = 5000010;
sort(b+1, b+n+1); int pos = unique(b+1, b+n+1) - b - 1;
for(int i = 1; i <= n; ++i) {
a[i] = lower_bound(b+1, b+pos+1, a[i]) - b;
++tong[a[i]];
}
for(int i = 1; i <= pos; ++i) sum[i] = sum[i-1] + tong[i];
int minn = 2147483647;
for(int i = 1; i <= pos; ++i) {
int pre = max(0, i - k);
if(n - (sum[i] - sum[pre]) < minn)
minn = n - (sum[i] - sum[pre]);
}
cout << minn << endl;
return 0;
}
D.Welfare State
第四题是我比较擅长的数据结构题,所以比较幸运的把它\(A\)了
翻译
有\(n\)个数,\(q\)个操作
操作有两种:
1 p x
:将\(p\)位置上的数变为\(x\)
2 x
:将所有小于\(x\)的数变为\(x\)
输出经过\(q\)次操作后的这\(n\)个数
题解
不知道官方正解是什么,不过我用的线段树
用线段树维护区间最小值,对于操作\(2\),直接修改\([1,n]\)的最小值并打上懒标记,对于操作\(1\),就直接单点修改并一路下传懒标记就可以了
输出就每个点单点询问+下传懒标记
算是比较基础的线段树题吧
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define ls p << 1
#define rs p << 1 | 1
#define mid ((l + r) >> 1)
using namespace std;
LL read() {
LL k = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9')
k = k * 10 + c - 48, c = getchar();
return k * f;
}
int tree[200010 << 2], tag[200010 << 2], a[200010];
inline void up(int p) {
tree[p] = min(tree[ls], tree[rs]);
}
void build(int l, int r, int p) {
if(l == r) {
tree[p] = a[l]; return ;
}
build(l, mid, ls); build(mid+1, r, rs);
up(p);
}
inline void down(int l, int r, int p) {
tag[ls] = max(tag[ls], tag[p]);
tag[rs] = max(tag[rs], tag[p]);
tree[ls] = max(tree[ls], tag[p]);
tree[rs] = max(tree[rs], tag[p]);
tag[p] = 0;
}
void update1(int pos, int l, int r, int p, int k) {
if(l == r) {
tree[p] = k; return ;
}
down(l, r, p);
if(pos <= mid) update1(pos, l, mid, ls, k);
else update1(pos, mid+1, r, rs, k);
up(p);
}
void update2(int nl, int nr, int l, int r, int p, int k) { //别问我为什么要写一个函数,我是智障
if(l >= nl && r <= nr) {
tree[p] = max(tree[p], k); tag[p] = max(tag[p], k);
return ;
}
down(l, r, p);
if(nl <= mid) update2(nl, nr, l, mid, ls, k);
if(nr > mid) update2(nl, nr, mid+1, r, rs, k);
up(p);
}
int query(int pos, int l, int r, int p) {
if(l == r) {
return tree[p];
}
down(l, r, p);
if(pos <= mid) return query(pos, l, mid, ls);
else return query(pos, mid+1, r, rs);
up(p);
}
int main() {
int n = read();
memset(tree, 127, sizeof(tree));
for(int i = 1; i <= n; ++i) a[i] = read();
build(1, n, 1);
int m = read();
while(m--) {
int opt = read(), x = read();
if(opt == 1) {
int k = read();
update1(x, 1, n, 1, k);
}
else update2(1, n, 1, n, 1, x);
}
for(int i = 1; i <= n; ++i)
printf("%d ", query(i, 1, n, 1));
return 0;
}
后记
作完这四道题就去睡觉了,躺在床上还担心会不会\(FST\),后来果然一语成谶,不过还好是\(C\)题而不是\(D\)题
醒来一看涨了\(143\)分,开心的像一个\(160\)斤的孩子