AtCoder Regular Contest 179 A~C

A.Partition(思维)

题意:

给你整数NNNKKK

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

  • Y0=0Y_0=0Y0=0
  • Yi=∑j=1iXj(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)

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

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

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

分析:

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

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

代码:

#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(动态规划)

题意:

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

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

  • 对于每个B=1,2,…,MB=1,2,\dots,MB=1,2,,MAAA(包括两端)中任何两个不同出现的BBB之间都存在值XBX_BXB

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

  • 对于每一对整数(l,r)(l,r)(l,r),如1≤l<r≤N1\leq l\lt r\leq N1l<rNAl=Ar=BA_l=A_r=BAl=Ar=B,都存在一个整数mmml≤m≤rl\leq m\leq rlmr),如Am=XBA_m=X_BAm=XB

分析:

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

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

代码:

#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(交互)

题意:

这是一个交互式问题

给你一个正整数NNN

评测机有一个隐藏的正整数RRRNNN整数A1,A2,…,ANA_1,A_2,\dots,A_NA1,A2,,AN。保证∣Ai∣≤R|A_i|\le RAiR∣∑i=1NAi∣≤R\left|\displaystyle\sum_{i=1}^{N}A_i\right|\le Ri=1NAiR

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

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

你无法直接知道RRRAiA_iAi的值,但可以与评测机互动最多250002500025000次。

对于正整数iii,让XiX_iXi成为写在黑板上的第iii个整数。具体来说,Xi=AiX_i=A_iXi=Aii=1,2,…,Ni=1,2,\dots,Ni=1,2,,N

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

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

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

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

RRRAiA_iAi的值是在程序与评测机开始交互之前确定的。

分析:

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

首先将序列排序,取头尾两数求和,产生一个新的数,将其插入剩余的序列中,由于剩下的序列也是升序的,可以二分插入,由此产生一个新的序列,对这个序列不断重复上述操作即可。这样只需要排序一次,再加上合并需要的次数,总次数为10000+10000+1000=2100010000+10000+1000=2100010000+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、付费专栏及课程。

余额充值