[CodeForces]Codeforces Round #428 (Div. 2)
A. Arya and Bran
题意
已知$n$天中一个人每天获得$a_i$块糖,但最多只能拿其中8块,多余的可以以后拿。判断最早哪一天能拿到$k$块。
题解
模拟每天的情况即可。
代码
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
int n = read(), k = read(), rst = 0;
for (int i = 1; i <= n; i++) {
int ai = read();
int dec = min(ai + rst, 8);
rst += (ai - dec);
k -= dec;
if (k <= 0) return 0 * printf("%d\n", i);
}
return 0 * printf("-1\n");
}
B. Game of the Rows
题意
已知飞机上有$n$座位,每排编号1~8号座位,其中1和2、3和4、4和5、5和6、7和8相邻;另有$k$队士兵,每队$a_i$人,规定不同部队的士兵不能坐相邻座位。判断飞机能否载走所有士兵。
题解
贪心。首先考虑大于等于3人队,优先把他们往3456位或12、78位上塞;再考虑两人队,优先塞到12、78位,塞不下则看成三人队塞到3456位,还塞不下则看成两个一人队;最后考虑一人队即可。
代码
#include <bits/stdc++.h>
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
int n = read(), k = read();
int cnt[5] = {0, 0, 0, 0, 0};
int rst[5] = {0, 0, 2 * n, 0, n};
for (int i = 1; i <= k; i++) {
int a = read();
while (a >= 3) {
if (rst[4] > 0) a -= 4, rst[4]--;
else
if (rst[2] > 0) a -= 2, rst[2]--;
else return 0 * printf("NO\n");
}
cnt[a]++;
}
while (cnt[2] > 0) {
if (rst[2] > 0) cnt[2]--, rst[2]--;
else
if (rst[4] > 0) cnt[2]--, rst[4]--, rst[1]++;
else cnt[2]--, cnt[1] += 2;
}
if (cnt[1] > rst[1] + rst[2] + rst[4] * 2) return 0 * printf("NO\n");
return 0 * printf("YES\n");
}
C. Journey
题意
已知一棵树,求从编号为1的根节点到达叶子节点距离的期望。
题解
直接遍历这棵树,记录每个节点的深度和到达它的概率,每到一个叶子结点累加它的深度$\times$概率即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+9;
double res = 0.0;
bool vis[N];
vector<int> g[N];
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
void dfs(int u, double d, double x) {
queue<int> q; double y = 0.0;
for (int v: g[u]) if (!vis[v]) q.push(v), y++;
if (q.empty()) res += d * x;
else {
while (!q.empty()) {
int v = q.front(); q.pop();
vis[v] = 1;
dfs(v, d + 1, x / y);
}
}
}
int main() {
int n = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
memset(vis, 0, sizeof(vis)); vis[1] = 1;
dfs(1, 0, 1);
return 0 * printf("%.8lf\n", res);
}
D. Winter is here
题意
已知$n$个数$a_1,a_2,...,a_n$,对于任意$k$个数的子集,若$i_1$ $<$ $i_2<...<$$i_k且gcd(a_{i_1},a_{i_2},...,a_{i_k})>1$,则这个子集的“力量”为$k\times gcd(a_{i_1},a_{i_2},...,a_{i_k})$。求所有子集的“力量”之和。
题解
直接考虑子集的最大公约数是多少然后统计时间复杂度显然很高,我们考虑一个数$i$整除子集中的所有数,那$i$一定整除子集的最大公约数,于是我们先统计能被$i$整除的子集个数。
假设$n$个数中能被$i$整除的个数为$x$,则能被$i$整除的子集个数$b_i=\sum_{k=0}^x k\times {\binom{x}{k}}=x\times \sum_{k=0}^{x-1} {\binom{x-1}{k}}=x\times {2^{x-1}}$,再根据$b_i$倒过来使用容斥原理求和即可。
代码
#include <bits/stdc++.h>
typedef long long ll;
const ll N = 1e6+9, Q = 1e9+7;
ll res, a[N], b[N], p[N];
inline ll read() {
ll s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int main() {
ll n = read();
for (ll i = 0; i < n; i++) {ll x = read(); a[x]++;}
memset(p, 0, sizeof(p)), p[0] = 1;
for (ll i = 1; i <= n; i++) p[i] = 2 * p[i - 1] % Q;
for (ll i = N - 1; i > 1; i--) {
ll x = 0;
for (ll j = i; j < N; j += i) x += a[j];
if (x >= 0) {
b[i] = x * p[x - 1] % Q;
for (ll j = i * 2; j < N; j += i) b[i] = (b[i] - b[j] + Q) % Q;
res += b[i] * i; res %= Q;
}
}
return 0 * printf("%lld\n", res);;
}
E. Mother of Dragons
题意
已知$n$个顶点的图和k,要求将$k$分配给各个顶点(可以为$0$),当两个顶点有边直接相连时将产生它们乘积的价值,求一个分配方案使得产生的乘积之和最大。
题解
最优解一定是将$k$平均分配给图中的一个最大团。简单证明如下:
首先对于两个顶点,由均值不等式,平均分配时产生的乘积最大,由此容易证到:对于团也是平均分配时的乘积和最大。
假设将$k$分配给一个顶点个数为$c$的团,则产生的乘积之和为$s=(\frac{k}{c})^{2}\times \frac{c(c-1)}{2}=\frac{k^2(c-1)}{2c}$。
现在有一个不属于该团的顶点$P$,它与团中顶点最多有$c-1$条边,则平均分配这$c+1$个顶点产生的乘积之和最大为$s'=(\frac{k}{c+1}){2}\times(\frac{c(c-1)}{2}+(c-1))=\frac{k2(c+2)(c-1)}{2(c+1)^2}$。
简单做差,可以推到$s>s'$。
即:$(1)$对于一个团,不要额外添加不属于它的顶点。
假设除了这个顶点个数为$c$的团之外,还有$m$个顶点数最多为$c$的团,则产生的乘积之和为$s''=\sum_{i=1}m(\frac{k}{\sum_{i=1}c_i})^{2}\times \frac{c(c-1)}{2}=(\frac{k}{\sum_{i=1}{m}c_i})\times \sum_{i=1}^m\frac{c_i(c_i-1)}{2}\leq (\frac{k}{\sum_{i=1}{m}c_i})\times \frac{c(c-1)}{2}$。
简单做差,可以推到$s\geq s''$。
即:$(2)$对于多个团,只要选其中的一个最大团。
根据$(1)(2)$得证。
最后找最大团可以通过$BronKerbosch$算法或者$dp$或者启发式搜索即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
const int N = 40, C = 20;
int n, k, dp[1 << C];
ll adj[N];
inline int read() {
int s = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return s * x;
}
int maxc(){
for (int i = 0; i < n; i++) {
for (int j = 0, x; j < n; j++) {
x = read(), adj[i] |= (ll) (x || i == j) << j;
}
}
for (int i = 1; i < (1 << max(0, n - C)); i++) {
int x = i;
for (int j = 0; j < C; j++) {
if ((i >> j) & 1) x &= adj[j + C] >> C;
}
if (x == i) dp[i] = __builtin_popcount(i);
}
for (int i = 1; i < (1 << max(0, n - C)); i++) {
for (int j = 0; j < C; j++) {
if ((i >> j) & 1) dp[i] = max(dp[i], dp[i ^ (1 << j)]);
}
}
int ret = 0;
for (int i = 0; i < (1 << min(C, n)); i++) {
int x = i, y = (1 << max(0, n - C)) - 1;
for (int j = 0; j < min(C, n); j++) {
if ((i >> j) & 1) x &= adj[j] & ((1 << C) - 1), y &= adj[j] >> C;
}
if (x != i) continue;
ret = max(ret, __builtin_popcount(i) + dp[y]);
}
return ret;
}
int main() {
n = read(), k = read();
int c = maxc();
ld x = (ld) k / c;
return 0 * printf("%.8Lf\n", x * x * c * (c - 1) / 2);
}