牛客多校第四场签到题题解

A Task Computing

题意:给定长度为 nn 的序列 {(wi,pi)}\{(w_i,p_i)\},从中选出 mm 项并重新排列得到子序列 {a1,a2,,am}\{a_1,a_2,\cdots,a_m\},最大化 i=1mwaij=1ipaj\displaystyle \sum_{i=1}^m w_{a_i} \prod_{j=1}^i p_{a_j}n1×105n \leq 1\times 10^5m20m \leq 20pi[0.8,1.2]p_i \in [0.8,1.2]

解法:如果选出的元素给定为子序列 {(wi,pi)}\{(w_i,p_i)\},考虑如何贪心。使用常见的相邻局部调整法证明贪心:交换相邻两个元素 (wi,pi)(w_i,p_i)(wi+1,pi+1)(w_{i+1},p_{i+1}),对最终答案的影响只会与 wiw_{i}wi+1w_{i+1} 的系数有关。原始系数为 a=wij=1ipj+wi+1j=1i+1pja=\displaystyle w_i\prod_{j=1}^{i} p_j+w_{i+1}\prod_{j=1}^{i+1}p_j,改变后系数为 b=wi+1pij=1i+1pj+wij=1i+1pjb=\displaystyle \dfrac{w_{i+1}}{p_i}\prod_{j=1}^{i+1} p_j+w_{i}\prod_{j=1}^{i+1}p_j。令 ab=(j=1i1pj)(wipi+wi+1pipi+1wi+1pi+1wipipi+1)>0\displaystyle a-b=\left(\prod_{j=1}^{i-1}p_j\right)(w_ip_i+w_{i+1}p_ip_{i+1}-w_{i+1}p_{i+1}-w_ip_ip_{i+1})>0,则 wi(pi+11)<wi+1(pi1)w_i(p_{i+1}-1)<w_{i+1}(p_i-1)。因而贪心规则为 wi(pi+11)w_i(p_{i+1}-1)

按照这一贪心排序,那么选择的元素必然按此顺序。为了便于计算,可以用秦九韶算法对上式进行倒序计算。设 fi,jf_{i,j} 为倒序遍历到第 ii 个元素,已经选择了 jj 个元素的最大值,则有 fi,jmax(fi+1,j,fi+1,j1pi+wi)f_{i,j} \leftarrow \max(f_{i+1,j},f_{i+1,j-1}p_i+w_i)。总时间复杂度 O(nlogn+nm)\mathcal O(n \log n+nm)

#include <bits/stdc++.h>
using namespace std;
const long double inf = 1e12;
struct node
{
    long long w;
    long double p;
    bool operator<(const node &b)const
    {
        return w * (b.p - 1) < b.w * (p - 1);
    }
};
const int N = 100000;
node que[N + 5];
long double f[N + 5][25];
int main()
{
    int n, m, p;
    long long w;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n;i++)
        scanf("%lld", &que[i].w);
    for (int i = 1; i <= n;i++)
    {
        scanf("%d", &p);
        que[i].p = 1.0 * p / 10000;
    }
    sort(que + 1, que + n + 1);
    f[n + 1][0] = 0;
    for (int i = 1; i <= m; i++)
        f[n + 1][i] = -inf;
    for (int i = n; i >= 0; i--)
        for (int j = 0; j <= m; j++)
        {
            f[i][j] = f[i + 1][j];
            if (j)
                f[i][j] = max(f[i][j], que[i].w + que[i].p * f[i + 1][j - 1]);
        }
    printf("%.12Lf\n", f[1][m]);
    return 0;
}

H Wall Builder II

题意:给定 nn,长度为 1×i1\times i 的砖块有 ni+1n-i+1 个。现将这些砖拿来砌墙,不允许旋转(只能 1×i1\times i 的放置)且要密铺,且要求砌成的墙为矩形,问这个大矩形的最小周长。n100n \leq 100

解法:面积 S=i=1ni(ni+1)\displaystyle S=\sum_{i=1}^n i(n-i+1)。因而枚举长宽 h,wh,w,贪心的从大到小的将 1×w1\times w 的区域填砖块,填满 hh 次即可。可以证明这样贪心对 n100n \leq 100 成立。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t, n;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        int s = 0;
        for (int i = 1; i <= n;i++)
            s += i * (n - i + 1);
        vector<pair<pair<int, int>, pair<int, int>>> put;
        function<bool(int, int)> check = [&](int w, int h)
        {
            put.clear();
            vector<int> cnt(n + 1);
            for (int i = 1; i <= n;i++)
                cnt[i] = n - i + 1;
            for (int times = 0; times < h; times++)
            {
                int last = w;
                while(last)
                {
                    bool flag = 0;
                    for (int j = min(n, last); j >= 1;j--)
                        if(cnt[j])
                        {
                            put.emplace_back(make_pair(times, w - last), make_pair(times + 1, w - last + j));
                            flag = 1;
                            last -= j;
                            cnt[j]--;
                            break;
                        }
                    if (!flag)
                        return false;
                }
            }
            return true;
        };
        int ans = 0;
        for (int i = sqrt(s); i >= 1;i--)
            if (s % i == 0 && (check(i, s / i) || check(s / i, i)))
            {
                ans = 2 * (i + s / i);
                break;
            }
        printf("%d\n", ans);
        for (auto i : put)
            printf("%d %d %d %d\n", i.first.second, i.first.first, i.second.second, i.second.first);
    }
    return 0;
}

K NIO's Sword

题意:初始 x=0x=0,给定 nn,每次执行操作 x10x+Ax \leftarrow 10x+AA[0,9]A \in [0,9],问使 xmodnx \bmod n11 依次变化到 nn 需要最少执行几次操作。n1×106n \leq 1\times 10^6

解法:从 iii+1i+110ki+yi+1(modn)10^ki+y \equiv i+1 \pmod n,其中 y<10ky <10^kkk 为操作次数。因而 yi(110k)+1(modn)y \equiv i(1-10^k)+1 \pmod n。因而枚举 kk 即可。注意 n=1n=1 时答案为 00

#include <bits/stdc++.h>
using namespace std;
long long th[20];
int main()
{
    th[0] = 1;
    for (int i = 1; i < 20;i++)
        th[i] = th[i - 1] * 10;
    int n;
    scanf("%d", &n);
    long long ans = 0;
    for (int i = 0; i < n;i++)
        for (int j = 0;;j++)
        {
            long long x = (1 - (th[j] - 1) * i % n + n) % n;
            if (x < th[j])
            {
                ans += j;
                break;
            }
        }
    printf("%lld", ans);
    return 0;
}

L Black Hole

题意:给定初始正 nn 面体和棱长,每次将其面心相连构成新正多面体,进行 kk 次,问最终是几面体、棱长多少。n200n \leq 200k20k \leq 20a1000a \leq 1000

解法:只有五种正多面体:正四、六、八、十二、二十面体。这种变化规律下,四面体还是四面体,正六、八面体互换,正十二、二十面体互换。这是因为正六面体有 88 个顶点,正八面体有 66 个顶点,正十二面体有 2020 个顶点,正二十面体有 1212 个顶点。

正四面体内缩之后边长变为 a3\dfrac{a}{3},正六面体内缩后边长变为 a2\dfrac{a}{\sqrt 2},正八面体内缩后边长变为 2a3\dfrac{\sqrt 2a}{3}

对于正十二面体,每个面为正五边形。考虑面心到棱的距离为 h=a2tan54h=\dfrac{a}{2} \tan 54^\circ,两个面的二面角为 θ=2arctan(ϕ+1)=2arcsin255\theta=2\arctan(\phi+1)=2\arcsin \sqrt{\dfrac{2}{5-\sqrt 5}},其中 ϕ=512\phi=\dfrac{\sqrt 5-1}{2},则面心距离为新的棱长,为 2hsinθ2=atan542552h \sin \dfrac{\theta}{2}=a \tan 54^\circ \sqrt{\dfrac{2}{5-\sqrt 5}}

对于正二十面体,每个面为等边三角形。面心到棱长距离 h=a23h=\dfrac{a}{2\sqrt 3},两个面二面角为 θ=2arctan(ϕ+2)=2arcsin1+523\theta=2\arctan(\phi+2)=2\arcsin \dfrac{1+\sqrt 5}{2\sqrt 3},因而面心距 2hsinθ2=a(1+5)62h\sin \dfrac{\theta}{2}=\dfrac{a(1+\sqrt 5)}{6}

#include <bits/stdc++.h>

using namespace std;
const int N = 205;
using db = double;
const db phi = (sqrt(5) - 1) / 2, pi = acos(-1);
int n, k, T[N];
db calc(db a, int t) {
    switch (t) {
    case 4: return a / 3;
    case 6: return a / sqrt(2);
    case 8: return a * sqrt(2) / 3;
    case 12: return a * tan(54 * pi / 180) * sin(atan(phi + 1));
    case 20: return a / sqrt(3) * sin(atan(phi + 2));
    }
    return -1;
}
void Solve() {
    db a;
    scanf("%d%lf%d", &n, &a, &k);
    if (!T[n]) return puts("impossible"), void();
    while (k--) a = calc(a, n), n = T[n];
    printf("possible %d %.12lf\n", n, a);
}
int main() {
    int t = 1;
    T[4] = 4, T[6] = 8, T[8] = 6, T[12] = 20, T[20] = 12;
    scanf("%d", &t);
    while (t--) Solve();
    return 0;
}

N Particle Arts

题意:给定序列 {an}\{a_n\},可以执行无穷多次操作:选定两个数字 ai,aja_i,a_j,然后 aiai&aja_i \leftarrow a_i \& a_jajaiaja_j \leftarrow a_i | a_j,问最大方差。n1×105 n \leq 1\times 10^5ai215a_i \leq 2^{15}

解法:ai+aj=(ai&aj)+(aiaj)a_i+a_j=(a_i\&a_j)+(a_i|a_j),因而 σ\sigma 不变。当 μ\mu 最大时一定是数字差距最大的。因而对于每一位分开考虑,若第 ii 位上有 kk11 则把最大的 kk 个数字加上 2i2^i,这样得到新序列 {bn}\{b_n\}。然后根据公式计算 μ=1n3i=1n(nbij=1nbj)2\displaystyle \mu=\dfrac{1}{n^3}\sum_{i=1}^n \left(nb_i-\sum_{j=1}^n b_j\right)^2 即可。注意计算过程中会爆 long long,需要使用 int_128

#include <bits/stdc++.h>
using namespace std;
void print(__int128_t x)
{
    string s;
    while(x)
    {
        s += x % 10 + 48;
        x /= 10;
    }
    if(s.empty())
        s += "0";
    reverse(s.begin(), s.end());
    cout << s;
}
int main()
{
    int n;
    scanf("%d", &n);
    vector<long long> a(n);
    long long sum = 0;
    for (int i = 0; i < n;i++)
    {
        scanf("%lld", &a[i]);
        sum += a[i];
    }
    vector<int> times(21, 0);
    for (auto i : a)
        for (int j = 0; j <= 20;j++)
            if (i & (1 << j))
                times[j]++;
    for (auto &i : a)
        i = 0;
    for (int i = 0; i <= 20;i++)
        for (int j = 0; j < times[i];j++)
            a[j] |= 1ll << i;
    __int128_t ans = 0;
    for (int i = 0; i < n;i++)
    {
        __int128_t now = a[i] * n - sum;
        ans += now * now;
    }
    __int128_t x = ans, y = (__int128_t)n * n * n;
    __int128_t g = __gcd(x, y);
    print(x / g);
    printf("/");
    print(y / g);
    return 0;
}
全部评论

相关推荐

FieldMatching:看成了猪头顾问,不好意思
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务