“蔚来杯“2022牛客暑期多校训练营4

"蔚来杯"2022牛客暑期多校训练营4

题目链接

N Particle Arts

题目大意

有n个粒子,每个粒子有一个能量ai。两两随机碰撞,每碰撞一次两粒子的能量变为a&b,a|b。求所有粒子能量稳定后的方差。

题解

首先观察&和|运算,0和0得00,0和1得01,1和0得01,1和1得11,可知运算以后0和1的数量没有变,更进一步可知,所有n个数字每一位上的总的0和1数量没有变,即n个数字的和不变。

同时根据题意可以得到,要想a&b和a|b以后稳定,结果一定为a和b。根据上面的&和|的结果可知,假如a和b的某一位(二进制)为0和1,那么1一定都在其中一个数字里,0一定都在另一个数字里,这样才能保证&和|以后结果为a和b。

所以可以统计n个数字里每一位有多少个1,然后重新将每一位的1分配给n个数字。分配规则:当计算第i个数字时,若某一位还有1则分配1,若没有则分配0。

最后是计算方差的公式:D(x)=E(x2)-E(x)2
代码

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull n, x, ans1, ans2, temp, gc;
ull cnt[20];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> x;
        ans1 += x;
        for (int j = 0, p = 1; j <= 15; j++, p <<= 1)
        {
            if (x & p)
                cnt[j]++;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        temp = 0;
        for (int j = 0, p = 1; j <= 15; j++, p <<= 1)
        {
            if (cnt[j])
            {
                cnt[j]--;
                temp += p;
            }
        }
        ans2 += temp * temp;
    }
    ans2 = n * ans2 - ans1 * ans1;
    n = n * n;
    gc = __gcd(n, ans2);
    cout << ans2 / gc << "/" << n / gc << endl;
    return 0;
}

K NIO’s Sword

题目大意

玩家初始有一把攻击力为0的剑,需要依次击杀n个敌人,仅当攻击力模n与i同余才能击杀第i个敌人。玩家可以升级剑,每次升级相当于在当前攻击力后面添加一个数字,问最少需要几次升级。

题解

记 Ai为击杀第i个怪物时的攻击力。

设为了击杀第 i 只怪物进行了 k 次升级,则有Ai-1 * 10k + x = Ai, 0 ≤ x < 10k ,即(i - 1) * 10k + x ≡ i mod n 。

所以对于每个 i 值,从小到大枚举 k 的取值,并计算 x 可取的最小非负值,直到找到一个满足条件的解即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, res;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (ll i = 1; i <= n; i++)
    {
        for (ll j = 0; j <= 6; j++)
        {
            if ((ll(i - (i - 1) * pow(10, j)) % n + n) % n < pow(10, j))
            {
                res += j;
                break;
            }
        }
    }
    cout << res << endl;
    return 0;
}

H Wall Builder II

题目大意

给n个1 * 1的矩形,n-1个1 * 2的矩形,n-2个1 * 3的矩形,……,1个1 * n的矩形,把这些矩形拼成一个大矩形,
求大矩形的最小周长以及可行的方案。

题解

首先可以求出大矩形面积S=n * (n+1) * (n+2) / 6,然后可以得出周长C=2 * (a + S / a),由对勾函数性质可得当a≤sqrt(S)(下取整),a尽量大时周长最小。

摆放的时候可以贪心,把所有小矩形从长到短放入大矩形,从上到下一行一行试着放,遇到某一行能放就
放。对于1到100中的所有n,这个贪心都可以构造出理论上的最优解。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 6e3 + 5;
int t, n, S, a, b;
int cnt[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++)
            cnt[i] = n - i + 1;
        S = n * (n + 1) * (n + 2) / 6;
        a = sqrt(S);
        while (S % a != 0)
        {
            a--;
        }
        b = S / a;
        cout << (a + b) * 2 << endl;
        if (a < b)
            swap(a, b);
        for (int i = 0; i < b; i++)
        {
            int temp = a;
            for (int j = n; j >= 1 && temp; j--)
            {
                while (cnt[j] && j <= temp)
                {
                    cout << a - temp << " " << i << " " << a - temp + j << " " << i + 1 << endl;
                    cnt[j]--;
                    temp -= j;
                }
            }
        }
    }
    return 0;
}

D Jobs (Easy Version)

题目大意

有 n 个公司,第 i 个公司有 mi 个工作,每个工作对三个能力分别有数值要求,必须三个能力都达标才能胜任这份工作。一个人只要能胜任一个公司的任意一份工作就可以去这个公司工作。询问 q 次,每次询问一个三元组,代表一个人三个能力的数值,求这个人可以去多少个公司工作。

题解

使用bitset存储,并处理前缀,单点查询即可,a[i] [iq] [eq] [aq]表示第i家公司,是否有分别小于等于iq,eq,aq的工作。

代码

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int maxn = 4e2 + 5;
const int mod = 998244353;
ll n, q, seed, m, x, y, z, res;
bitset<maxn> a[15][maxn][maxn];
int solve(int iq, int eq, int aq)
{
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if (a[i][iq][eq][aq])
            ans++;
    }
    return ans;
}
ll qpow(ll a, ll n, ll mod)
{
    a %= mod;
    ll res = 1;
    while (n)
    {
        if (n & 1)
            res = (res * a) % mod;
        a = (a * a) % mod;
        n >>= 1;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> m;
        while (m--)
        {
            cin >> x >> y >> z;
            a[i][x][y][z] = 1;
        }
    }
    for (int c = 1; c <= n; c++)
    {
        for (int i = 0; i <= 400; i++)
        {
            for (int j = 0; j <= 400; j++)
            {
                for (int k = 0; k <= 400; k++)
                {
                    if (a[c][i][j][k])
                    {
                        a[c][i + 1][j][k] = 1;
                        a[c][i][j + 1][k] = 1;
                        a[c][i][j][k + 1] = 1;
                    }
                }
            }
        }
    }
    cin >> seed;
    std::mt19937 rng(seed);
    std::uniform_int_distribution<> u(1, 400);
    int lastans = 0;
    for (int i = 1; i <= q; i++)
    {
        int IQ = (u(rng) ^ lastans) % 400 + 1;
        int EQ = (u(rng) ^ lastans) % 400 + 1;
        int AQ = (u(rng) ^ lastans) % 400 + 1;
        lastans = solve(IQ, EQ, AQ);
        res = (res + lastans * qpow(seed, q - i, mod)) % mod;
    }
    cout << res << endl;
    return 0;
}

A Task Computing

题目大意

从n个任务中选m个(a1, a2,…… , am)出来并任意排序,收益是
在这里插入图片描述

,求最大收益。

题解

[大佬链接](2022牛客暑期多校训练营4 个人题解 更新至5题 - 知乎 (zhihu.com))

观察收益式子可得,如果已经选了一些物品,其权值为x,现在需要往前面添加一个(w,p)的物品。那么权值会变成w+p*x。例如,现在有三件物品标号为1,2,3,收益为w1+w2p1+w3p1p2,若现在在前面添加一个物品标号为0,则收益为w0+w1p0+w2p0p1+w3p0p1p2,即w0+p0(w1+w2p1+w3p1p2)。

现在有两个物品 i , j ,如果i放到j前面获得的权值要大一点,则需要满足wi+pi(wj+pjx)>wj+pj(wi+pix),

化简一下得到,wi+piwj>wj+pjwi,wi(1-pj)>wj(1-pi),再除一下,可以发现大小结果只和自己有关。

所以可以排序,使得排在前面的数,更适合放到前面。

然后直接背包即可,定义f[i] [j] 为[i,n]中选择j个物品的最大值。

代码

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int maxn = 1e5 + 5;
const int Max = 0x3f3f3f3f;
struct Node
{
    double w, p;
    bool operator<(const Node &a) const
    {
        return w * (1 - a.p) > a.w * (1 - p);
    }
} a[maxn];
int n, m;
double dp[maxn][25];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i].w;
    for (int i = 1; i <= n; i++)
        cin >> a[i].p, a[i].p /= 10000;
    sort(a + 1, a + 1 + n);
    memset(dp, -Max, sizeof dp);
    dp[n + 1][0] = 0;
    for (int i = n; i >= 1; i--)
    {
        dp[i][0] = 0;
        for (int j = 0; j <= m; j++)
            dp[i][j + 1] = max(dp[i + 1][j + 1], a[i].w + a[i].p * dp[i + 1][j]);
    }
    cout << fixed << setprecision(10) << dp[1][m] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值