这次比赛必须要仔细反省,不能再出现暑假的精神状态。
A – 服务器需求
假设我们有很多服务器了,我们现在按天考虑,进行服务器的分配:假设有一个服务器编号集合\(\{id_i\}\),考虑一天天分配若干台,这样就可以保证每一台服务器的分配中不会出现同样的日期。
这样就可以分配完了,直接就有\(\sum a_i \leq |id_i| m\)。然后就是傻逼代码了。
// A.cpp
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAX_N = 4e5 + 200;
ll n, m, q, ai[MAX_N];
int main()
{
ll ans = 0;
multiset<ll> ms;
scanf("%lld%lld%lld", &n, &m, &q);
for (int i = 1; i <= n; i++)
scanf("%lld", &ai[i]), ms.insert(ai[i]), ans += ai[i];
printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m))));
while (q--)
{
ll x, y;
scanf("%lld%lld", &x, &y);
ans -= ai[x], ms.erase(ms.find(ai[x]));
ms.insert(ai[x] = y), ans += ai[x];
printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m))));
}
return 0;
}
// A.cpp
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAX_N = 4e5 + 200;
ll n, m, q, ai[MAX_N];
int main()
{
ll ans = 0;
multiset<ll> ms;
scanf("%lld%lld%lld", &n, &m, &q);
for (int i = 1; i <= n; i++)
scanf("%lld", &ai[i]), ms.insert(ai[i]), ans += ai[i];
printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m))));
while (q--)
{
ll x, y;
scanf("%lld%lld", &x, &y);
ans -= ai[x], ms.erase(ms.find(ai[x]));
ms.insert(ai[x] = y), ans += ai[x];
printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m))));
}
return 0;
}
// A.cpp #include <bits/stdc++.h> typedef long long ll; using namespace std; const int MAX_N = 4e5 + 200; ll n, m, q, ai[MAX_N]; int main() { ll ans = 0; multiset<ll> ms; scanf("%lld%lld%lld", &n, &m, &q); for (int i = 1; i <= n; i++) scanf("%lld", &ai[i]), ms.insert(ai[i]), ans += ai[i]; printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m)))); while (q--) { ll x, y; scanf("%lld%lld", &x, &y); ans -= ai[x], ms.erase(ms.find(ai[x])); ms.insert(ai[x] = y), ans += ai[x]; printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m)))); } return 0; }
B – 沙漠点列
这道题我在考场上只打了 60 分的特判分,还是很好打的。
考虑正解:先求出多少个连通块,然后再处理出有多少个环,来处理\(limit\)极大的情况;当\(limit\)无法直接包揽全部,那么我们优先把所有的割边进行贡献,然后再来破环贡献割边。用 DFS 的方式找出单元环(如果用 Tarjan 直接找就会 GG),然后排序从大到小选即可。
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200, MAX_E = 2e6 + 2000;
int head[MAX_N], current, n, m, limit, ncnt, dfn[MAX_N], low[MAX_N], ptot, stk[MAX_N], tail;
int SCC, aff[MAX_N], dist[MAX_N], loops[MAX_N], loop_cnt;
struct edge
{
int to, nxt;
} edges[MAX_E << 1];
int read()
{
char ch = getchar();
int x = 0, f = 1;
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return f * x;
}
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++ptot, stk[++tail] = u;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
if (dfn[edges[i].to] == 0)
tarjan(edges[i].to, u), low[u] = min(low[u], low[edges[i].to]);
else
low[u] = min(low[u], dfn[edges[i].to]);
if (dfn[u] == low[u])
{
SCC++;
do
{
aff[stk[tail]] = SCC;
} while (stk[tail--] != u);
}
}
void find_loop(int u, int fa)
{
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
if (dist[edges[i].to] == 0)
{
dist[edges[i].to] = dist[u] + 1;
find_loop(edges[i].to, u);
}
else if (dist[edges[i].to] < dist[u] + 1)
loops[++loop_cnt] = dist[u] - dist[edges[i].to] + 1;
}
int main()
{
memset(head, -1, sizeof(head));
n = read(), m = read(), limit = read();
for (int i = 1, u, v; i <= m; i++)
u = read(), v = read(), addpath(u, v), addpath(v, u);
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
tarjan(i, 0), ncnt++;
// get the non-loop component;
ncnt = SCC - ncnt;
if (limit <= ncnt)
printf("%d\n", SCC + limit - ncnt), exit(0);
limit -= ncnt;
for (int i = 1; i <= n; i++)
if (dist[i] == 0)
dist[i] = 1, find_loop(i, 0);
int ans = SCC;
sort(loops + 1, loops + 1 + loop_cnt);
reverse(loops + 1, loops + 1 + loop_cnt);
for (int i = 1; i <= loop_cnt; i++)
{
limit--;
ans += min(limit, loops[i] - 1);
limit -= loops[i] - 1;
if (limit < 0)
break;
}
printf("%d\n", ans);
return 0;
}
// B.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200, MAX_E = 2e6 + 2000;
int head[MAX_N], current, n, m, limit, ncnt, dfn[MAX_N], low[MAX_N], ptot, stk[MAX_N], tail;
int SCC, aff[MAX_N], dist[MAX_N], loops[MAX_N], loop_cnt;
struct edge
{
int to, nxt;
} edges[MAX_E << 1];
int read()
{
char ch = getchar();
int x = 0, f = 1;
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return f * x;
}
void addpath(int src, int dst)
{
edges[current].to = dst, edges[current].nxt = head[src];
head[src] = current++;
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++ptot, stk[++tail] = u;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
if (dfn[edges[i].to] == 0)
tarjan(edges[i].to, u), low[u] = min(low[u], low[edges[i].to]);
else
low[u] = min(low[u], dfn[edges[i].to]);
if (dfn[u] == low[u])
{
SCC++;
do
{
aff[stk[tail]] = SCC;
} while (stk[tail--] != u);
}
}
void find_loop(int u, int fa)
{
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
if (dist[edges[i].to] == 0)
{
dist[edges[i].to] = dist[u] + 1;
find_loop(edges[i].to, u);
}
else if (dist[edges[i].to] < dist[u] + 1)
loops[++loop_cnt] = dist[u] - dist[edges[i].to] + 1;
}
int main()
{
memset(head, -1, sizeof(head));
n = read(), m = read(), limit = read();
for (int i = 1, u, v; i <= m; i++)
u = read(), v = read(), addpath(u, v), addpath(v, u);
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
tarjan(i, 0), ncnt++;
// get the non-loop component;
ncnt = SCC - ncnt;
if (limit <= ncnt)
printf("%d\n", SCC + limit - ncnt), exit(0);
limit -= ncnt;
for (int i = 1; i <= n; i++)
if (dist[i] == 0)
dist[i] = 1, find_loop(i, 0);
int ans = SCC;
sort(loops + 1, loops + 1 + loop_cnt);
reverse(loops + 1, loops + 1 + loop_cnt);
for (int i = 1; i <= loop_cnt; i++)
{
limit--;
ans += min(limit, loops[i] - 1);
limit -= loops[i] - 1;
if (limit < 0)
break;
}
printf("%d\n", ans);
return 0;
}
// B.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e6 + 200, MAX_E = 2e6 + 2000; int head[MAX_N], current, n, m, limit, ncnt, dfn[MAX_N], low[MAX_N], ptot, stk[MAX_N], tail; int SCC, aff[MAX_N], dist[MAX_N], loops[MAX_N], loop_cnt; struct edge { int to, nxt; } edges[MAX_E << 1]; int read() { char ch = getchar(); int x = 0, f = 1; while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return f * x; } void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } void tarjan(int u, int fa) { dfn[u] = low[u] = ++ptot, stk[++tail] = u; for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != fa) if (dfn[edges[i].to] == 0) tarjan(edges[i].to, u), low[u] = min(low[u], low[edges[i].to]); else low[u] = min(low[u], dfn[edges[i].to]); if (dfn[u] == low[u]) { SCC++; do { aff[stk[tail]] = SCC; } while (stk[tail--] != u); } } void find_loop(int u, int fa) { for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != fa) if (dist[edges[i].to] == 0) { dist[edges[i].to] = dist[u] + 1; find_loop(edges[i].to, u); } else if (dist[edges[i].to] < dist[u] + 1) loops[++loop_cnt] = dist[u] - dist[edges[i].to] + 1; } int main() { memset(head, -1, sizeof(head)); n = read(), m = read(), limit = read(); for (int i = 1, u, v; i <= m; i++) u = read(), v = read(), addpath(u, v), addpath(v, u); for (int i = 1; i <= n; i++) if (dfn[i] == 0) tarjan(i, 0), ncnt++; // get the non-loop component; ncnt = SCC - ncnt; if (limit <= ncnt) printf("%d\n", SCC + limit - ncnt), exit(0); limit -= ncnt; for (int i = 1; i <= n; i++) if (dist[i] == 0) dist[i] = 1, find_loop(i, 0); int ans = SCC; sort(loops + 1, loops + 1 + loop_cnt); reverse(loops + 1, loops + 1 + loop_cnt); for (int i = 1; i <= loop_cnt; i++) { limit--; ans += min(limit, loops[i] - 1); limit -= loops[i] - 1; if (limit < 0) break; } printf("%d\n", ans); return 0; }
总结
这场比赛严重暴露了思路垃圾的弱点,之后每天都会固定写几道 CF 的贪心保持智商。