Codeforces Round #538 (Div. 2)题解报告
Problem A:Got Any Grapes?
题意
三个瓜皮吃三种葡萄,分别只能吃一种,两种,三种,问能不能都吃够数。
题解
按题意贪心即可。
代码
#include<bits/stdc++.h>
#define Error return puts("NO"), 0
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int x, y, z, a, b, c;
int main() {
a = ri(); b = ri(); c = ri();
x = ri(); y = ri(); z = ri();
x -= a; if(x < 0) Error;
y += x; y -= b; if(y < 0) Error;
z += y; z -= c; if(z < 0) Error;
puts("YES");
return 0;
}
Problem B:Yet Another Array Partitioning Task
题意
给一个数列让你划分成 k k k段,使得每段前 m m m大之和最大,并输出方案。
题解
直接选前 m ∗ k m*k m∗k大的数作为答案,凑够了 m m m个数就划分一段即可。
代码
#include<bits/stdc++.h>
const int N = 2e5 + 10;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int a[N], id[N], p[N], n, m, k, tp;
bool ok[N];
bool cmp(int x, int y) {return a[x] > a[y];}
int main() {
n = ri(); m = ri(); k = ri(); long long sum = 0;
for(int i = 1;i <= n; ++i)
a[i] = ri(), id[i] = i;
std::sort(id + 1, id + n + 1, cmp);
for(int i = 1;i <= m * k; ++i)
ok[id[i]] = true, sum += a[id[i]];
int cnt = m;
for(int i = 1;i <= n; ++i) {
cnt -= ok[i];
if(!cnt) p[++tp] = i, cnt = m;
}
printf("%lld\n", sum);
for(int i = 1;i < tp; ++i)
printf("%d ", p[i]); puts("");
return 0;
}
Problem C:Trailing Loves (or L’oeufs?)
题意
问十进制的 n ! n! n!在 B B B进制下尾巴有多少个 0 0 0
题解
实际上就是
B
x
∣
∣
n
!
B^x|| n!
Bx∣∣n!,求
x
x
x
B
=
p
1
a
1
p
2
a
2
⋯
p
n
a
n
B=p_1^{a_1}p_2^{a_2}\cdots p_n^{a_n}
B=p1a1p2a2⋯pnan
考虑求
p
i
x
i
∣
∣
n
!
p_i^{x_i}||n!
pixi∣∣n!,那么
x
=
min
{
x
i
a
i
}
x=\min \{\frac{x_i}{a_i}\}
x=min{aixi}
而对于每个
p
i
p_i
pi,
x
i
=
∑
k
⌊
n
x
k
⌋
x_i=\sum_k\lfloor\frac{n}{x^k} \rfloor
xi=∑k⌊xkn⌋(考虑
1
⋯
n
1\cdots n
1⋯n以内有多少个数整除
x
,
x
2
⋯
x
k
x,x^2\cdots x^k
x,x2⋯xk即可)
x
i
x_i
xi本质上是
n
n
n在
p
i
p_i
pi进制下的数位和。
代码
#include<bits/stdc++.h>
const int N = 2e6 + 10;
long long ri() {
char c = getchar(); long long x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
long long ans, n, b, pr[N];
int tp, a[N];
long long Calc(long long n, long long p) {
long long ans = 0;
for(n /= p;n; n /= p)
ans += n;
return ans;
}
int main() {
n = ri(); b = ri(); int m = sqrt(b);
for(int i = 2;i <= m && b != 1; ++i)
if(!(b % i)) {
pr[++tp] = i;
for(;!(b % i); b /= i) ++a[tp];
}
if(b > 1) pr[++tp] = b, a[tp] = 1;
long long ans = 1e18;
for(int i = 1;i <= tp; ++i)
ans = std::min(ans, Calc(n, pr[i]) / a[i]);
printf("%lld\n", ans);
return 0;
}
Problem D:Flood Fill
题意
给你一个序列,每个位置上有一种颜色,每次你可以将若干个连续且颜色相同的位置变成另外一种相同的颜色,求将整个序列变成同一种颜色最少步数。
题解
经典区间Dp,首先将相同颜色的连续块缩起来, f l , r f_{l,r} fl,r表示将 [ l , r ] [l,r] [l,r]变成同一种颜色的最少步数,考虑最后一步是怎么干的,一定有某种最优方案的最后一步是把整个区间变成 a l a_l al或 a r a_r ar的颜色(否则交换一下操作序列)。如果 a l ≠ a r a_l \neq a_r al̸=ar要么把 [ l + 1 , r ] [l+1,r] [l+1,r]变成 l l l的颜色,要么把 [ l , r − 1 ] [l,r-1] [l,r−1]变成 r r r的颜色。否则的话还可以将 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r−1]变成 a l , a r a_l,a_r al,ar的颜色。写个区间Dp即可。
代码
#include<bits/stdc++.h>
const int N = 5e3 + 10;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int f[N][N], c[N], n;
int main() {
n = ri(); int _n = 0;
for(int i = 1;i <= n; ++i) {
c[i] = ri();
if(c[i] != c[i - 1])
c[++_n] = c[i];
}
n = _n;
for(int i = 1;i < n; ++i)
f[i][i + 1] = 1;
for(int L = 3;L <= n; ++L)
for(int l = 1; l <= n - L + 1; ++l) {
int r = l + L - 1;
f[l][r] = std::min(f[l][r - 1], f[l + 1][r]);
if(c[l] == c[r]) f[l][r] = std::min(f[l][r], f[l + 1][r - 1]);
++f[l][r];
}
printf("%d\n", f[1][n]);
return 0;
}
Problem E:Arithmetic Progression
题意
交互题,给一个序列是由某个公差为正的整数等差数列打乱而来的,仅仅给你这个序列的长度,每次可询问 > x >x >x表示询问序列中时候有严格大于 x x x的数或 ? x ?x ?x表示询问序列中第 x x x个数,求首项和公差。询问次数不超过 60 60 60
题解
考场没时间想了 q w q qwq qwq, > x >x >x明摆这让你二分求最大值,大概花了 30 30 30次,剩下的那个就是只能随机得到这个等差数列的某个数。相当于是给你 30 30 30个数让你求公差。显然唯一的限制只有公差必须是两个数差的因子。因此排序相邻两个数相减之后去 g c d gcd gcd即可。错误的概率题解有给,大概是 1 0 − 9 10^{-9} 10−9级别。
代码
#include<bits/stdc++.h>
const int N = 1e6 + 10;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int Rand() {return (rand() << 15) + rand();}
int n, tp, st[100], ok[N], mx, d, cnt = 60;
int main() {
srand(time(0));
n = ri();
int L = -1e9, R = 1e9;
for(;L <= R;) {
int m = L + R >> 1;
printf("> %d\n", m);
fflush(stdout); --cnt;
ri() ? L = m + 1 : (mx = m, R = m - 1);
}
for(int i = 1;cnt && i <= n; ++i, --cnt) {
int x = Rand() % n + 1;
for(;ok[x];) x = Rand() % n + 1;
printf("? %d\n", x); ok[x] = true;
fflush(stdout);
st[++tp] = ri();
}
std::sort(st + 1, st + tp + 1);
if(st[tp] != mx) st[++tp] = mx;
for(int i = 1;i < tp; ++i)
d = std::__gcd(d, st[i + 1] - st[i]);
int mn = mx - d * (n - 1);
printf("! %d %d\n", mn, d);
fflush(stdout);
return 0;
}
Problem F:Please, another Queries on Array?
题意
区间乘法和询问某区间积的欧拉函数对 1 0 9 + 7 10^9+7 109+7去模。数大小不超过 300 300 300。
题解
由定义式可以得到
ϕ
(
n
)
=
n
∏
i
k
(
1
−
1
p
i
)
\phi(n)=n\prod_i^k(1-\frac{1}{p_i})
ϕ(n)=n∏ik(1−pi1)
n
n
n用线段树直接维护,一共只有
60
60
60个质因子,套个
b
i
t
s
e
t
bitset
bitset即可。
代码
#include<bits/stdc++.h>
#define ls p << 1
#define rs p << 1 | 1
const int N = 4e5 + 10, P = 1e9 + 7;
typedef std::bitset<63> BI;
bool vis[301];
BI t[N << 2], tgt[N << 2], nwt;
int s[N << 2], tgs[N << 2], pr[301], iv[301], a[N], tp, nws, n, q;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int add(int a, int b) {return a += b, a >= P ? a - P : a;}
int fix(int x) {return (x >> 31 & P) + x;}
int mul(int a, int b) {return 1LL * a * b % P;}
int Pow(int x, int k) {
int r = 1;
for(;k; x = mul(x, x), k >>= 1)
if(k & 1)
r = mul(r, x);
return r;
}
void Pre(int N) {
for(int i = 2;i <= N; ++i) {
if(!vis[i])
pr[tp++] = i;
for(int j = 0;j < tp && i * pr[j] <= N; ++j) {
vis[i * pr[j]] = true;
if(!(i % pr[j])) break;
}
}
}
BI Div(int x) {
BI c;
for(int i = 0;i < tp; ++i)
if(!(x % pr[i]))
c[i] = true;
return c;
}
void Tag(int p, int len, int f) {
tgs[p] = mul(tgs[p], tgs[f]);
tgt[p] |= tgt[f];
s[p] = mul(s[p], Pow(tgs[f], len));
t[p] |= tgt[f];
}
void Add(int p) {
nws = mul(nws, s[p]);
nwt |= t[p];
}
void Up(int p) {
s[p] = mul(s[ls], s[rs]);
t[p] = t[ls] | t[rs];
}
void Push(int p, int L, int m, int R) {
Tag(ls, m - L + 1, p);
Tag(rs, R - m, p);
tgs[p] = 1; tgt[p].reset();
}
void Build(int p, int L, int R) {
tgs[p] = 1;
if(L == R) {
t[p] = Div(a[L]);
s[p] = a[L];
return ;
}
int m = L + R >> 1;
Build(ls, L, m); Build(rs, m + 1, R);
Up(p);
}
void Modify(int p, int L, int R, int st, int ed) {
if(L == st && ed == R)
return Tag(p, R - L + 1, 0);
int m = L + R >> 1; Push(p, L, m, R);
if(st <= m) Modify(ls, L, m, st, std::min(ed, m));
if(ed > m) Modify(rs, m + 1, R, std::max(st, m + 1), ed);
Up(p);
}
void Query(int p, int L, int R, int st, int ed) {
if(L == st && ed == R)
return Add(p);
int m = L + R >> 1; Push(p, L, m, R);
if(st <= m) Query(ls, L, m, st, std::min(ed, m));
if(ed > m) Query(rs, m + 1, R, std::max(st, m + 1), ed);
}
int main() {
Pre(300);
iv[1] = 1;
for(int i = 2;i <= 300; ++i)
iv[i] = mul(iv[P % i], fix(- (P / i)));
for(int i = 1;i <= 300; ++i)
iv[i] = mul(iv[i], i - 1);
n = ri(); q = ri();
for(int i = 1;i <= n; ++i)
a[i] = ri();
Build(1, 1, n);
for(;q--;) {
char op = getchar(); for(;op != 'T' && op != 'M'; op = getchar()) ;
if(op == 'T') {
int l = ri(), r = ri();
nws = 1; nwt.reset();
Query(1, 1, n, l, r);
for(int i = 0;i < tp; ++i)
if(nwt[i])
nws = mul(nws, iv[pr[i]]);
printf("%d\n", nws);
}
else {
int l = ri(), r = ri();
tgs[0] = ri(); tgt[0] = Div(tgs[0]);
Modify(1, 1, n, l, r);
}
}
return 0;
}
线段树打得不够溜啊 q w q qwq qwq,最后一题如果切的稍微快一点倒二就有机会想出来惹。。。不过。。。。我也是第一次在 C F CF CF上交交互题T_T,果然是太菜了。