Codeforces Round #615 (Div. 3) (CF1294D、E、F)题解
A、B、C直接上代码吧
A
#include <bits/stdc++.h>
using namespace std;
signed main() {
int T; scanf ("%d", &T);
while (T--) {
int a, b, c, n;
scanf ("%d %d %d %d", &a, &b, &c, &n);
int s = a + b + c + n;
if (s % 3 == 0 && s / 3 >= max (a, max (b, c))) puts ("YES");
else puts ("NO");
}
return 0;
}
B
#include <bits/stdc++.h>
using namespace std;
struct node {
int x, y;
bool operator < (const node &t) {
return x == t.x ? y < t.y : x < t.x;
}
} p[1024];
char ch[100000];
signed main() {
int T; scanf ("%d", &T);
while (T--) {
int n; scanf ("%d", &n);
for (int i = 1; i <= n; ++i) scanf ("%d %d", &p[i].x, &p[i].y);
sort (p + 1, p + n + 1);
int s = 0, h = 0;
bool ok = 1;
for (int i = 1; i <= n; ++i) {
if (p[i].y < h) {ok = 0; break;}
for (int j = 1; j <= p[i].x - p[i - 1].x; ++j) ch[++s] = 'R';
for (int j = 1; j <= p[i].y - p[i - 1].y; ++j) ch[++s] = 'U';
h = max (h, p[i].y);
}
if (ok) {
puts ("YES");
for (int i = 1; i <= s; ++i) cout << ch[i]; puts ("");
}else puts ("NO");
}
return 0;
}
C
#include <bits/stdc++.h>
using namespace std;
const int N = 1E5, M = 1E9;
int k[N], p[N], tot, c[N];
inline void Pre_work () {
for (int i = 2; i * i <= M; ++i) {
if (!k[i]) p[++tot] = i;
for (int j = 1; j <= tot && 1ll * i * i * p[j] * p[j] <= M; ++j) {
k[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}
inline int Pow (int a, int b) {
int res (1);
while (b) {
if (b & 1) res *= a;
b >>= 1, a *= a;
} return res;
}
signed main() {
int T; scanf ("%d", &T);
Pre_work ();
while (T--) {
int n, a (0), b (0), c (0); scanf ("%d", &n); int t = n;
for (int i = 1; i <= tot; ++i) {
int cnt = 0, mx = 1;
while (n % p[i] == 0) n /= p[i], ++cnt;
if (!cnt) continue;
if (!a) {
a = Pow (p[i], mx);
cnt -= mx, ++mx;
if (cnt >= mx) {
b = Pow (p[i], mx), cnt -= mx, ++mx;
if (t / a / b != a && t / a / b != b && t / a / b > 1) {c = t / a / b; break;}
}
}
else if (!b) {
b = Pow (p[i], mx);
cnt -= mx, ++mx;
if (t / a / b != a && t / a / b != b && t / a / b > 1) {c = t / a / b; break;}
}
else if (!c) {
if (t / a / b != a && t / a / b != b && t / a / b > 1) {c = t / a / b; break;}
} else break;
}
if (c) puts ("YES"), printf ("%d %d %d\n", a, b, c);
else puts ("NO");
}
return 0;
}
D
题意:向序列中每次加入一个数,可以把序列中的任意一个数a变为b,满足b=a+k*x,b>=0且k为整数,每次回答当前序列中不存在的最小自然数的最大可能值
显然与取模运算有关,a[i]表示%x结果为i的数中最小的没有的数,为了让最小的数最大,一定先从小的数开始向上填补空缺,即假设有m个数%n=i,则让第一个数为i,第二个为x+i,第三个为2*x+i......
当加入一个数p的时候,就让a[p%x]加上x,a[i]的初值为i,得到的答案最优,最终答案为所有a[i]的min值
由于蒟蒻比较菜,想都不想就直接上线段树了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5;
int a[N], mx;
int c[N << 2];
void build (int p, int l, int r) {
if (l == r) {c[p] = l; return;}
int mid (l + r >> 1);
build (p << 1, l, mid), build (p << 1 | 1, mid + 1, r);
c[p] = min (c[p << 1], c[p << 1 | 1]);
}
inline void update (int p, int l, int r, int pos, int val) {
// printf ("%d %d %d\n", p, l, r);
if (l == r) {c[p] = val; return;}
int mid = l + r >> 1;
pos <= mid ? update (p << 1, l, mid, pos, val) : update (p << 1 | 1, mid + 1, r, pos, val);
c[p] = min (c[p << 1], c[p << 1 | 1]);
}
signed main() {
int n, x;
scanf ("%lld %lld", &n, &x);
for (int i = 0; i < x; ++i) a[i] = i; mx = 0;
build (1, 0, x - 1);
for (int i = 1, y; i <= n; ++i) {
scanf ("%lld", &y);
if (y > 0) y %= x; a[y] += x;
update (1, 0, x - 1, y, a[y]);
printf ("%lld\n", c[1]);
}
return 0;
}
E
题意:给定一个矩阵,有两种操作:1、改变某个位置的数 2、将任意一列“滚动一位”(具体看题面),使其转换到目标矩阵
由题意易得每列互不相关,对每列单独考虑
在一列中,有些数本来就是目标状态中这一列的数,可以通过若干次移动操作使其在正确的位置,有些数则只能修改,统计这一列如果移动 k 次(0 <= k <= n - 1)有多少数不需要修改
设第 i 行第 j 列的数为 x,若 x <= n * m && (x - j) % m == 0, 说明 x 在目标状态中是第 j 列第 (x - j) / m + 1 行的数,(注意这里一定要判断x <= n * m,比赛时没判断查了半天也没发现)
可以通过 p = (i - (x - j) / m - 1 + n) % n 次移动操作使 x 在正确的位置(+ n 在 % n 是为了处理负数)并在计数数组 c 中使 c[p]++
最后枚举移动次数 k,这一列需要的步骤 ans = min (k + n - c[k]),并将 ans 累加到最终答案res中
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5;
int a[N], mx;
int c[N << 2];
void build (int p, int l, int r) {
if (l == r) {c[p] = l; return;}
int mid (l + r >> 1);
build (p << 1, l, mid), build (p << 1 | 1, mid + 1, r);
c[p] = min (c[p << 1], c[p << 1 | 1]);
}
inline void update (int p, int l, int r, int pos, int val) {
// printf ("%d %d %d\n", p, l, r);
if (l == r) {c[p] = val; return;}
int mid = l + r >> 1;
pos <= mid ? update (p << 1, l, mid, pos, val) : update (p << 1 | 1, mid + 1, r, pos, val);
c[p] = min (c[p << 1], c[p << 1 | 1]);
}
signed main() {
int n, x;
scanf ("%lld %lld", &n, &x);
for (int i = 0; i < x; ++i) a[i] = i; mx = 0;
build (1, 0, x - 1);
for (int i = 1, y; i <= n; ++i) {
scanf ("%lld", &y);
if (y > 0) y %= x; a[y] += x;
update (1, 0, x - 1, y, a[y]);
printf ("%lld\n", c[1]);
}
return 0;
}
F
题意:在树上找三个点a,b,c,使a到b,b到c,c到a的路径上覆盖的边权之和最大(一条边算一次)
先贪心求出一条直径,把直径上边权改为0,再从直径两个端点分别出发找距离最远的点取最大值
#include <bits/stdc++.h>
using namespace std;
inline void read (int &x) {
char ch = getchar(); x = 0;
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
}
const int N = 2e5 + 10;
int n;
int cnt, to[N << 1], nxt[N << 1], h[N], d[N << 1];
inline void add (int u, int v) {
to[++cnt] = v, d[cnt] = 1, nxt[cnt] = h[u], h[u] = cnt;
}
int tmp, mx, c[N], fr[N], fd[N];
void dfs (int u, int la, int deep) {
if (deep > mx) mx = deep, tmp = u;
for (int i = h[u]; i; i = nxt[i])
if (to[i] != la) fr[to[i]] = u, fd[to[i]] = i, dfs (to[i], u, deep + d[i]);
}
int res;
signed main() {
read (n); cnt = 1;
for (int i = 1, u, v; i < n; ++i)
read (u), read (v), add (u, v), add (v, u);
dfs (1, 0, 0); mx = 0; fr[tmp] = 0;
int a = tmp; dfs (tmp, 0, 0); int b = tmp; res = mx;
while (fr[tmp]) d[fd[tmp]] = d[fd[tmp] ^ 1] = 0, tmp = fr[tmp];
mx = 0; tmp = 0;
dfs (a, 0, 0); dfs (b, 0, 0);
int c = tmp; if (!c) c = 1;
while (c == a || c == b) ++c;
printf ("%d\n", mx + res);
printf ("%d %d %d\n", a, b, c);
return 0;
}