[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);
}
posted @ 2017-08-16 16:09  jstztzy  阅读(238)  评论(0编辑  收藏  举报