Codeforces Round #679 (Div. 2)

203 篇文章 0 订阅
164 篇文章 0 订阅
传送门

Codeforces Round #679 (Div. 2)

A

n n n 为偶数,那么考虑相邻数对组合为零,则有 a i × b i + a i + 1 × b i + 1 = 0 a_i\times b_i+a_{i+1}\times b_{i+1}=0 ai×bi+ai+1×bi+1=0;取 b i = a i + 1 , b i + 1 = − a i b_i=a_{i+1},b_{i+1}=-a_i bi=ai+1,bi+1=ai 即可。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 105
int A[maxn], B[maxn];

int main()
{
    int t, n;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        for (int i = 0; i < n; ++i)
            scanf("%d", A + i);
        for (int i = 0; i < n; i += 2)
            B[i] = A[i + 1], B[i + 1] = -A[i];
        for (int i = 0; i < n; ++i)
            printf("%d%c", B[i], i + 1 == n ? '\n' : ' ');
    }
    return 0;
}
B

考虑到矩阵中元素各不相同,任取一行的首元素,在打乱的列中找到这个元素,那么对应的列即第一列;枚举列首至列尾的元素,找到所在的行,即可恢复原矩阵。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 505
int R[maxn][maxn], C[maxn][maxn], res[maxn][maxn];

int main()
{
    int t, n, m;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                scanf("%d", R[i] + j);
        int tmp = R[0][0], c;
        for (int i = 0; i < m; ++i)
        {
            for (int j = 0; j < n; ++j)
            {
                scanf("%d", C[i] + j);
                if (C[i][j] == tmp)
                    c = i;
            }
        }
        for (int i = 0; i < n; ++i)
        {
            int num = C[c][i];
            for (int j = 0; j < n; ++j)
            {
                if (R[j][0] == num)
                {
                    for (int k = 0; k < m; ++k)
                        res[i][k] = R[j][k];
                    break;
                }
            }
        }
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                printf("%d%c", res[i][j], j + 1 == m ? '\n' : ' ');
    }
    return 0;
}
C

考虑找到一个区间 [ l , r ) [l,r) [l,r),使 j ∈ [ l , r ) j\in [l,r) j[l,r) 满足对 n n n 个琴音都有 a i + j a_i+j ai+j,目标是使区间长度最小。那么枚举 6 6 6 根弦以及 n n n 个琴音,计算 b i − a j b_i-a_j biaj;坐标离散化后在对应位置记录琴音的索引,问题转化为求满足区间 [ l , r ) [l,r) [l,r) n n n 个琴音索引都出现的区间最小值。尺取法求解即可。

#include <algorithm>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define maxn 100005
int N, A[6], B[maxn], cnt[maxn], id[maxn * 6], X[maxn * 6], sX[maxn * 6];
vector<int> axis[maxn * 6];

int main()
{
    for (int i = 0; i < 6; ++i)
        scanf("%d", A + i);
    scanf("%d", &N);
    for (int i = 0; i < N; ++i)
        scanf("%d", B + i);
    int k = 0;
    for (int i = 0; i < 6; ++i)
        for (int j = 0; j < N; ++j)
        {
            sX[k] = X[k] = B[j] - A[i], id[k++] = j;
        }
    sort(X, X + k);
    int n = unique(X, X + k) - X;
    for (int i = 0; i < k; ++i)
    {
        sX[i] = lower_bound(X, X + n, sX[i]) - X;
        axis[sX[i]].push_back(id[i]);
    }
    int res = INT_MAX, s = 0, t = 0, num = 0;
    for (;;)
    {
        while (t < n && num < N)
        {
            for (int x : axis[t])
            {
                if (!cnt[x])
                    ++num;
                ++cnt[x];
            }
            ++t;
        }
        if (num < N)
            break;
        res = min(res, X[t - 1] - X[s]);
        for (int x : axis[s])
        {
            --cnt[x];
            if (!cnt[x])
                --num;
        }
        ++s;
    }
    printf("%d\n", res);
    return 0;
}
D 补题

考虑某次购买,此时价格为 p r i c e price price,那么商店剩余的飞镖价格都大于 p r i c e price price;即在不补货的情况下,其后购买的飞镖价格 p r i c e ′ > p r i c e price'>price price>price。考虑逆向遍历,此时遍历到每一次购买信息时,都已经获取其后购买的信息;那么可以用 小根堆 维护购买飞镖的价格,逆时间顺序遍历到补货操作时,补入小根堆的最小值。

此时得到了一个可能解,但不能保证购买时都是选择费用最小的飞镖。那么正向遍历,依然用 小根堆 维护补货的价格,与购买操作进行对比,若相同则为满足条件的一个解;反之则不是。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define maxn 100005
struct query
{
    char op;
    int i, x;
} Q[maxn << 1];
int N, res[maxn];

int main()
{
    scanf("%d", &N);
    for (int i = 0, j = 0; i < (N << 1); ++i)
    {
        scanf(" %c", &Q[i].op);
        if (Q[i].op == '-')
            scanf("%d", &Q[i].x);
        else
            Q[i].i = j, ++j;
    }
    priority_queue<int, vector<int>, greater<int>> q;
    bool f = 1;
    for (int i = (N << 1) - 1; i >= 0; --i)
    {
        if (Q[i].op == '+')
            if (q.empty())
            {
                f = 0;
                break;
            }
            else
            {
                res[Q[i].i] = q.top();
                q.pop();
            }
        else
            q.push(Q[i].x);
    }
    if (f)
    {
        for (int i = 0; i < (N << 1); ++i)
        {
            if (Q[i].op == '+')
                q.push(res[Q[i].i]);
            else
            {
                if (q.empty() || Q[i].x != q.top())
                {
                    f = 0;
                    break;
                }
                q.pop();
            }
        }
    }
    if (f)
    {
        puts("YES");
        for (int i = 0; i < N; ++i)
            printf("%d%c", res[i], i + 1 == N ? '\n' : ' ');
    }
    else
        puts("NO");
    return 0;
}
E 补题

若存在上界,只可能出现在 k × d , k ∈ Z k\times d,k\in Z k×d,kZ 时刻。设 f ( k ) f(k) f(k) 为时刻 k × d , k ∈ Z k\times d,k\in Z k×d,kZ 打掉的血量与恢复值的差,容易观察到 f ( k ) f(k) f(k) 是一个凸函数,二分/三分法 求解即可。

在时刻 k × d , k ∈ Z k\times d,k\in Z k×d,kZ,打掉的血量为 ( k + 1 ) × a (k+1)\times a (k+1)×a。回复的血量考虑区间 [ i × d + 1 , i × d + c ] [i\times d+1,i\times d+c] [i×d+1,i×d+c] 是否完全包含在 [ 0 , k × d ] [0,k\times d] [0,k×d] 两种情况讨论:若为否,则有 d × i + c ≤ d × k d\times i+c\leq d\times k d×i+cd×k,在 d × k − c ≥ 0 d\times k-c\geq 0 d×kc0 时有解;若为是,每一个相邻的区间左端点相距 d d d,等差数列求和即可。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll a, b, c, d;

ll f(ll n)
{
    ll res = 0, i = -1;
    res += (n + 1) * a;
    if (d * n - c >= 0)
    {
        i = (d * n - c) / d;
        res -= (i + 1) * c * b;
    }
    ll l = d * n - d * (i + 1), r = d;
    res -= ((l + r) * (n - i - 1) / 2) * b;
    return res;
}

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
        ll lb = -1, ub = 1e6 + 1;
        while (ub - lb > 1)
        {
            ll mid = (lb + ub) >> 1;
            if (f(mid) >= f(mid + 1))
                ub = mid;
            else
                lb = mid;
        }
        printf("%lld\n", ub == 1e6 + 1 ? -1 : f(ub));
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值