AtCoder Regular Contest 179 A~C

A.Partition(思维)

题意:

给你整数 N N N K K K

长度为 N N N的整数序列 X = ( X 1 , X 2 , … , X N ) X=(X_1,X_2,\dots,X_N) X=(X1,X2,,XN)累积和定义为长度为 N + 1 N+1 N+1的序列 Y = ( Y 0 , Y 1 , … , Y N ) Y=(Y_0,Y_1,\dots,Y_N) Y=(Y0,Y1,,YN)如下:

  • Y 0 = 0 Y_0=0 Y0=0
  • Y i = ∑ j = 1 i X j ( i = 1 , 2 , … , N ) Y_i=\displaystyle\sum_{j=1}^{i}X_j (i=1,2,\dots,N) Yi=j=1iXj(i=1,2,,N)

长度为 N N N的整数序列 X = ( X 1 , X 2 , … , X N ) X=(X_1,X_2,\dots,X_N) X=(X1,X2,,XN) 如果且仅当它满足以下条件时,才称作良好序列

  • X X X的累积和中,任何小于 K K K的值都出现在任何不小于 K K K的值之前。
  • 形式上,对于 X X X的累积和 Y Y Y,对于任意一对整数 ( i , j ) (i,j) (i,j),若 0 ≤ i , j ≤ N 0\le i,j\le N 0i,jN,若 ( Y i < K (Y_i\lt K (Yi<K Y j ≥ K ) Y_j\ge K) YjK),则 i < j i\lt j i<j

给你一个长度为 N N N的整数序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,,AN)。请判断 A A A中的元素能否重新排列成一个良好序列。如果可以,输出一个这样的重排结果。

分析:

首先题意告诉我们,小于等于 K K K的在前面,大于 K K K的在后面,那么不难想到,当 K K K是正数的时候,可以按从小到大的顺序排列。因为有 Y 0 = 0 Y_0=0 Y0=0,当 K K K是负数时,按从小到大顺序排列便不合法,需要从大到小排列。

考虑如何判定无解的情况,当K小于等于 0 0 0时,因为 Y 0 = 0 Y_0=0 Y0=0,若想要从大到小排列,那么 Y 0 = 0 Y_0=0 Y0=0就是最大数,需要保证排列的和大于等于 K K K,若和小于 K K K,则不合法。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

int main() {
    LL n, k, sum = 0;
    cin >> n >> k;
    vector<LL> a(n);
    for (LL i = 0; i < n; i++)
        cin >> a[i], sum += a[i];
    if (sum < k && k <= 0) {
        cout << "No" << endl;
        return 0;
    }
    if (k > 0)
        sort(a.begin(), a.end());
    else
        sort(a.begin(), a.end(), greater<LL>());
    cout << "Yes" << endl;
    for (LL i = 0; i < n; i++)
        cout << a[i] << " ";
    cout << endl;
    return 0;
}

B.Between B and B(动态规划)

题意:

给你一个长度为 M M M的序列 ( X 1 , X 2 , … , X M ) (X_1,X_2,\dots,X_M) (X1,X2,,XM),它由介于 1 1 1 M M M之间的整数组成。

求长度为 N N N的序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,,AN)中,序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,,AN)的个数对 998244353 998244353 998244353取模的结果,该序列由 1 1 1 M M M之间的整数组成,且满足以下条件:

  • 对于每个 B = 1 , 2 , … , M B=1,2,\dots,M B=1,2,,M A A A(包括两端)中任何两个不同出现的 B B B之间都存在值 X B X_B XB

更正式地说,对于每个 B = 1 , 2 , … , M B=1,2,\dots,M B=1,2,,M,必须满足以下条件:

  • 对于每一对整数 ( l , r ) (l,r) (l,r),如 1 ≤ l < r ≤ N 1\leq l\lt r\leq N 1l<rN A l = A r = B A_l=A_r=B Al=Ar=B,都存在一个整数 m m m l ≤ m ≤ r l\leq m\leq r lmr),如 A m = X B A_m=X_B Am=XB

分析:

读题考虑DP,观察数据范围, 1 ≤ M ≤ 10 1\leq M \leq 10 1M10 1 ≤ N ≤ 1 0 4 1\leq N \leq 10^4 1N104,猜测本题复杂度可能是 O ( N × 2 M × M ) O(N\times 2^M\times M) O(N×2M×M),考虑如何设计状态转移。

f i , j f_{i,j} fi,j表示前 i i i个数,状态为 j j j,简单的想,对于每一个数 B B B,存储从上一个数到这个一个数的关系,例如用 0 0 0表示没有, 1 1 1表示有,那么看一下 X B X_B XB是否存在即可,但是这样写,复杂度为 ( 2 10 ) 10 (2^{10})^{10} (210)10,无法存储。但是我们发现其实并不需要所有数的状态,只需要 X B X_B XB这一个状态,只需要判断 X B X_B XB是否存在即可。我们让状态 j j j的第 k k k位存储 x k x_k xk是否存在,第 i i i个数如果填 k k k,然后查看上一个填 k k k的位置,从上一个 k k k到当前的 k k k x k x_k xk是否一直存在。考虑如何更新,因为第 i i i个数填了 k k k,所以 j j j里面 k k k这一位就要清空。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 1e4 + 10;
const int mod = 998244353;
const int M = 11;

LL n, m;
LL a[M], f[N][1 << M], mask[M];

int main() {
    cin >> m >> n;
    for (LL i = 1; i <= m; i++)
        cin >> a[i], mask[a[i]] |= 1ll << i - 1;
    f[0][(1 << m) - 1] = 1;
    for (LL i = 0; i < n; i++)
        for (LL j = 1; j <= m; j++)
            for (LL k = 0; k < 1 << m; k++)
                if ((k >> j - 1) & 1)
                    f[i + 1][(k ^ (1 << j - 1)) | mask[j]] = (f[i + 1][(k ^ (1 << j - 1)) | mask[j]] + f[i][k]) % mod;
    LL res = 0;
    for (LL i = 0; i < 1 << m; i++)
        res = (res + f[n][i]) % mod;
    cout << res << endl;
    return 0;
}

C.Beware of Overflow(交互)

题意:

这是一个交互式问题

给你一个正整数 N N N

评测机有一个隐藏的正整数 R R R N N N整数 A 1 , A 2 , … , A N A_1,A_2,\dots,A_N A1,A2,,AN。保证 ∣ A i ∣ ≤ R |A_i|\le R AiR ∣ ∑ i = 1 N A i ∣ ≤ R \left|\displaystyle\sum_{i=1}^{N}A_i\right|\le R i=1NAi R

有一块黑板,可以在上面书写绝对值不超过 R R R的整数。最初,黑板是空的。

评测机在黑板上依次写下了数值 A 1 , A 2 , … , A N A_1,A_2,\dots,A_N A1,A2,,AN。你的任务是经过若干操作后使黑板上只包含一个值 ∑ i = 1 N A i \displaystyle\sum_{i=1}^{N}A_i i=1NAi

你无法直接知道 R R R A i A_i Ai的值,但可以与评测机互动最多 25000 25000 25000次。

对于正整数 i i i,让 X i X_i Xi成为写在黑板上的第 i i i个整数。具体来说, X i = A i X_i=A_i Xi=Ai i = 1 , 2 , … , N i=1,2,\dots,N i=1,2,,N

在一次交互中,可以指定两个不同的正整数 i i i j j j并选择以下操作之一:

  • 执行加法运算。评测机将擦除黑板上的 X i X_i Xi X j X_j Xj并在黑板上写下 X i + X j X_i+X_j Xi+Xj
    ∣ X i + X j ∣ ≤ R |X_i+X_j|\leq R Xi+XjR必须保持不变。
  • 进行比较。评测机会告诉你 X i < X j X_i\lt X_j Xi<Xj是真还是假。

在这里,在每次交互开始时,写在黑板上的第 i i i和第 j j j个整数必须没有被擦除。

适当地进行交互操作,以便在所有交互操作之后,黑板上只包含一个值 ∑ i = 1 N A i \displaystyle\sum_{i=1}^{N}A_i i=1NAi

R R R A i A_i Ai的值是在程序与评测机开始交互之前确定的。

分析:

首先我们想到进行排序,可以发现 ∣ A 1 + A n ∣ ≤ R |A_1+A_n|\le R A1+AnR一定满足,分情况讨论可证,发现这个性质后,我们可以进一步处理问题。

首先将序列排序,取头尾两数求和,产生一个新的数,将其插入剩余的序列中,由于剩下的序列也是升序的,可以二分插入,由此产生一个新的序列,对这个序列不断重复上述操作即可。这样只需要排序一次,再加上合并需要的次数,总次数为 10000 + 10000 + 1000 = 21000 10000+10000+1000=21000 10000+10000+1000=21000,满足要求。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

bool cmp(LL a, LL b) {
    cout << "? " << a << " " << b << endl;
    LL ok;
    cin >> ok;
    return ok;
}

int main() {
    LL n;
    cin >> n;
    vector<LL> id;
    for (LL i = 1; i <= n; i++)
        id.push_back(i);
    sort(id.begin(), id.end(), cmp);
    while (n > 1) {
        cout << "+ " << id[0] << " " << id.back() << endl;
        LL p;
        cin >> p;
        id.erase(id.begin());
        id.pop_back();
        n--;
        if (n == 1)
            break;
        LL l = 0, r = id.size() - 1;
        while (l < r) {
            LL mid = l + r >> 1;
            if (cmp(p, id[mid]))
                r = mid;
            else
                l = mid + 1;
        }
        if (!cmp(p, id[r]))
            r++;
        id.insert(id.begin() + r, p);
    }
    cout << "!" << endl;
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值