Codeforces Round 757 (Div.2) 解题报告

传送门

C o d e f o r c e s   R o u n d   757   ( D i v . 2 ) \rm Codeforces ~ Round ~ 757 ~ (Div. 2) Codeforces Round 757 (Div.2)

A Divan and a Store

n n n 个物品,第 i i i 个商品的价格为 a i a_i ai

给你 l l l r r r,表示商人只会选择价格在 [ l , r ] [l,r] [l,r] 内的物品。

商人有 k k k 元钱,问你商人最多可以买多少个商品。

T T T 组数据。

对于 100 % 100\% 100% 的数据,保证 1 ≤ T ≤ 100 , 1 ≤ n ≤ 100 , 1 ≤ l ≤ r ≤ 1 0 9 , 1   l e q k ≤ 1 0 9 , 1 ≤ a i ≤ 1 0 9 1\leq T \leq 100,1 \leq n \leq 100,1 \leq l \leq r \leq 10^9,1 \ leq k \leq 10^9,1 \leq a_i \leq 10^9 1T100,1n100,1lr109,1 leqk109,1ai109

sol

贪心,每次买剩下的价格最小的商品。

首先选出满足条件的物品,放到优先队列里(最小值优先)。

然后一件一件弹出来,计算钱数,若钱数 > k > k >k 或队列为空,结束程序。

时间复杂度 O ( T n log ⁡ n ) \mathcal O(Tn \log n) O(Tnlogn)

#include <bits/stdc++.h>

using namespace std;

#define int long long

int t;

int n, l, r, k;

signed main()
{
    scanf("%lld", &t);
    while (t--)
    {
        scanf("%lld%lld%lld%lld", &n, &l, &r, &k);
        priority_queue<int, vector<int>, greater<int>> q;
        for (int i = 1, a; i <= n; ++i)
        {
            scanf("%lld", &a);
            if (a < l || a > r)
                continue;
            q.push(a);
        }
        int ans = 0, sum = 0;
        while (!q.empty())
        {
            int now = q.top();
            q.pop();
            sum += now;
            // cout << now << "\n";
            if (sum <= k)
                ans++;
            else
                break;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

B Divan and a New Project

在一条街上,盖 n + 1 n+1 n+1 栋楼,每栋楼坐标为 x i x_i xi,满足 ∀ j ≠ i , x i ≠ x j \forall j \ne i,x_i \ne x_j j=i,xi=xj x i x_i xi 是一个整数。

将所有楼从 0 0 0 n n n 标号。

有一人在编号为 0 0 0 的楼,分别要去编号为 i i i 的建筑 a i a_i ai 次,这个人往返编号为 i i i 的建筑一趟花费的时间为 2 × ( ∣ x i − x 0 ∣ ) 2 \times(|x_i - x_0|) 2×(xix0)

请安排每一栋楼的坐标,使得这个人花费的时间最短。

T T T 组数据。

对于 100 % 100\% 100% 的数据,保证 1 ≤ T ≤ 1 0 3 , 1 ≤ n ≤ 2 ∗ 1 0 5 , 0 ≤ a i ≤ 1 0 6 1\leq T \leq 10^3,1 \leq n \leq 2*10^5,0 \leq a_i \leq 10^6 1T103,1n2105,0ai106

sol

首先,我们把编号为 0 0 0 的楼的坐标定为 0 0 0 方便去做。

记答案为 a n s ans ans,则 a n s = ∑ i = 1 n 2 ∗ a i ∗ ∣ x 0 − x i ∣ ans=\sum\limits_{i=1}^{n}{2*a_i*|x_0-x_i|} ans=i=1n2aix0xi

要使得 a n s ans ans 最少,就要让 a i a_i ai 大的楼放到距离 x 0 x_0 x0 近的地方。

那么按照 a i a_i ai 从大到小排序,在 x 0 x_0 x0 按照左右左右左右…的顺序放楼即可。

具体实现见代码。

时间复杂度 O ( T n ) \mathcal O(Tn) O(Tn)

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int _ = 2e5 + 7;

int t;

int n;

struct abc
{
    int v, id;
} a[_];

int b[_];

int ans;

bool cmp(abc a, abc b)
{
    return a.v > b.v;
}

signed main()
{
    scanf("%lld", &t);
    while (t--)
    {
        ans = 0;
        scanf("%lld", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%lld", &a[i].v), a[i].id = i;
        sort(a + 1, a + n + 1, cmp);
        int k = 0;
        for (int i = 1; i <= n; i += 2)
        {
            b[a[i].id] = ++k;
        }
        k = 0;
        for (int i = 2; i <= n; i += 2)
        {
            b[a[i].id] = --k;
        }
        for (int i = 1; i <= n; ++i)
        {
            ans += 2 * a[i].v * abs(b[a[i].id]);
        }
        cout << ans << "\n";
        cout << "0 ";
        for (int i = 1; i <= n; ++i)
            cout << b[i] << " ";
        cout << "\n";
    }
    return 0;
}

C Divan and bitwise operations

有一个未知的序列 a a a,现知道 m m m 个信息,每个星信息为 l r x 的形式给出,表示区间 [ l , r ] [l,r] [l,r] 的按位或为 x x x,保证 a a a 中每一个数都被包含在区间 [ l , r ] [l,r] [l,r] 至少一次。

请输出序列 a a a 的所有子序列的异或和   m o d   ( 1 0 9 + 7 ) \bmod (10^9+7) mod(109+7)

T T T 组数据。

对于 100 % 100\% 100% 的数据,保证 1 ≤ T ≤ 1 0 3 , 1 ≤ n , m ≤ 2 ∗ 1 0 5 , 1 ≤ l ≤ r ≤ n , 0 ≤ x ≤ 2 30 − 1 1\leq T \leq 10^3,1 \leq n,m \leq 2*10^5,1 \leq l \leq r \leq n,0 \leq x \leq 2^{30}-1 1T103,1n,m2105,1lrn,0x2301

sol

显然,我们可以得到整个序列的按位或就是所有 x x x 的按位或,设为 S S S

如果 S S S 的第 i i i 位为 0 0 0,贡献即为 0 0 0

否则总有一个 1 1 1,当中恰有一个对应贡献为 2 i 2^{i} 2i,总贡献为 2 i × 2 n − 1 2^{i}\times2^{n-1} 2i×2n1

那么 A n s = S × 2 n − 1 Ans=S \times 2^{n-1} Ans=S×2n1

时间复杂度 O ( T n ) \mathcal O(Tn) O(Tn)

具体实现见代码。

#include <bits/stdc++.h>

#define int long long

using namespace std;

inline int read()
{
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar())
        if (c == '-')
            w = -1;
    for (; isdigit(c); c = getchar())
        s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

const int mod = 1e9 + 7;

inline int qpow(int x, int y)
{
    int res = 1;
    while (y)
    {
        if (y & 1)
            res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}

int T, n, m;

signed main()
{
    T = read();
    while (T--)
    {
        n = read(), m = read();
        int ans = 0;
        while (m--)
        {
            read(), read();
            ans = ans | read();
        }
        ans = ans * qpow(2, n - 1) % mod;
        printf("%lld\n", ans);
    }
    return 0;
}

D Divan and Kostomuksha

给定序列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,要求重排 a a a,使得

∑ i = 1 n gcd ⁡ ( a 1 , a 2 , . . . , a i ) \sum\limits_{i=1}^n \gcd(a_1,a_2,...,a_i) i=1ngcd(a1,a2,...,ai)

最大。

输出这个最大值。

sol

显然,一道 dp

c n t i cnt_i cnti 表示数组 a a a 中是 i i i 的倍数的元素个数, d p i dp_i dpi 表示时能得到已含有因数 i i i 结尾排列能获得的最大值。

此时有转移方程
d p i = max ⁡ j = 1 f i × p r i j + i × ( c n t i − c n t i × p r i j ) dp_i=\max_{j=1}{f_{i \times pri_j} + i \times (cnt_i - cnt_{i \times pri_j})} dpi=j=1maxfi×prij+i×(cnticnti×prij)
最后答案为满足 c n t i = n cnt_i=n cnti=n d p i dp_i dpi 的最大值。

c n t cnt cnt 可先筛出所有质数,再枚举 i i i 和质数集 p r i pri pri,计算 c n t i cnt_i cnti

时间复杂度 O ( w log ⁡ log ⁡ w ) \mathcal O(w \log \log w) O(wloglogw),其中 w w w 为值域。

具体实现见代码。

#include <bits/stdc++.h>

using namespace std;

#define int long long

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}

const int _ = 2e7 + 1;

int n;

int ans;

int cnt[_ + 7];

int dp[_ + 7];

bool vis[_];

vector<int> primes;

void init()
{
    for (int i = 2; i < _; ++i)
    {
        if (!vis[i])
            primes.push_back(i);
        for (auto p : primes)
        {
            if (p * i > _)
                break;
            vis[p * i] = 1;
            if (i % p == 0)
                break;
        }
    }
}

signed main()
{
    init();
    n = read();
    for (int i = 1, x; i <= n; ++i)
        cnt[read()]++;
    for (auto p : primes)
        for (int j = _ / p; j >= 1; --j)
            cnt[j] += cnt[j * p];
    for (int i = _ - 1; i >= 1; --i)
    {
        dp[i] = cnt[i] * i;
        for (auto p : primes)
        {
            int j = p * i;
            if (j > _)
                break;
            dp[i] = max(dp[i], dp[j] + i * (cnt[i] - cnt[j]));
        }
        if (cnt[i] == n)
            ans = max(ans, dp[i]);
    }
    printf("%lld\n", ans);
    return 0;
}

E Divan and a Cottage

给定 n n n 天的气温 T i T_i Ti,设第 i i i 天温度为 P P P,则第 i + 1 i+1 i+1 天的温度为:

  • P + 1 ( P < T i ) P+1 ( P < T_i) P+1(P<Ti)

  • P − 1 ( P > T i ) P-1 ( P >T_i) P1(P>Ti)

  • P ( P = T i ) P(P = T_i) P(P=Ti)

对第 i i i 天有 k i k_i ki 个询问,问若第 0 0 0 天的温度为 x x x ,那么第 i i i 天的温度是多少。

强制在线。

sol

显然,所有初始的室温变化后的结果满足单调。

那用一棵动态开点值域线段树维护所有初始的室温变化后的结果即可。

具体地,每次插入一个气温 T i T_i Ti 后,在线段树上二分以得到变化后的室温为 T i T_i Ti 的区间 [ l , r ] \left[l,r\right] [l,r],之后 [ − 1 , l ) [-1,l) [1,l) 区间 + 1 +1 +1 ( r , M a x T + 1 ] \left(r,MaxT+1\right] (r,MaxT+1] 区间 − 1 -1 1 即可。

时间复杂度 O ( n log ⁡ w ) \mathcal{O}(n \log w) O(nlogw),其中 w w w 为值域。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

inline ll read()
{
    ll x = 0, f = 1;
    char c = getchar();
    while ((c < '0' || c > '9') && (c != '-'))
        c = getchar();
    if (c == '-')
        f = -1, c = getchar();
    while (c >= '0' && c <= '9')
        x = x * 10 + c - '0', c = getchar();
    return x * f;
}

const int N = 2e5 + 10, mod = 1e9 + 1;

int n, m;

const int M = 4e7 + 10;

int tot, mn[M], mx[M], tag[M], ls[M], rs[M];

inline int New(int l, int r)
{
    ++tot, mn[tot] = l, mx[tot] = r;
    return tot;
}

inline void upd(int u, int x)
{
    mn[u] += x, mx[u] += x, tag[u] += x;
}

inline void push_down(int u, int l, int r, int mid)
{
    if (!tag[u])
        return;
    if (!ls[u])
        ls[u] = New(l, mid);
    if (!rs[u])
        rs[u] = New(mid + 1, r);
    upd(ls[u], tag[u]), upd(rs[u], tag[u]);
    tag[u] = 0;
}

inline void update(int &u, int l, int r, int ql, int qr, int x)
{
    if (ql > qr)
        return;
    if (!u)
        u = New(l, r);
    if (l >= ql && r <= qr)
    {
        upd(u, x);
        return;
    }
    int mid = l + r >> 1;
    push_down(u, l, r, mid);
    if (ql <= mid)
        update(ls[u], l, mid, ql, qr, x);
    if (qr > mid)
        update(rs[u], mid + 1, r, ql, qr, x);
    mn[u] = min(!ls[u] ? l : mn[ls[u]], !rs[u] ? mid + 1 : mn[rs[u]]);
    mx[u] = max(!ls[u] ? mid : mx[ls[u]], !rs[u] ? r : mx[rs[u]]);
}

inline int Findl(int u, int l, int r, int x)
{
    if ((!u ? l : mn[u]) >= x)
        return -1;
    if (l == r)
        return l;
    int mid = l + r >> 1;
    push_down(u, l, r, mid);
    int ret = Findl(rs[u], mid + 1, r, x);
    if (ret != -1)
        return ret;
    return Findl(ls[u], l, mid, x);
}

inline int Findr(int u, int l, int r, int x)
{
    if ((!u ? r : mx[u]) <= x)
        return mod;
    if (l == r)
        return l;
    int mid = l + r >> 1;
    push_down(u, l, r, mid);
    int ret = Findr(ls[u], l, mid, x);
    if (ret != mod)
        return ret;
    return Findr(rs[u], mid + 1, r, x);
}

inline int Query(int u, int l, int r, int ql)
{
    if (l == r)
        return !u ? l : mn[u];
    int mid = l + r >> 1;
    push_down(u, l, r, mid);
    if (ql <= mid)
        return Query(ls[u], l, mid, ql);
    return Query(rs[u], mid + 1, r, ql);
}

signed main()
{
    n = read();
    ll lasans = 0;
    int rt = 0;
    for(int i = 1; i <= n; ++i)
    {
        int x = read();
        int l = Findl(rt, 0, 1e9, x), r = Findr(rt, 0, 1e9, x);
        update(rt, 0, 1e9, 0, l, +1);
        update(rt, 0, 1e9, r, 1e9, -1);
        m = read();
        for(int j = 1; j <= m; ++j)
        {
            int x = (read() + lasans) % mod;
            printf("%lld\n", lasans = Query(rt, 0, 1e9, x));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值