A.Partition(思维)
题意:
给你整数NNN和KKK。
长度为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=1∑iXj(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 N0≤i,j≤N,若(Yi<K(Y_i\lt K(Yi<K和Yj≥K)Y_j\ge K)Yj≥K),则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),它由介于111和MMM之间的整数组成。
求长度为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取模的结果,该序列由111和MMM之间的整数组成,且满足以下条件:
- 对于每个B=1,2,…,MB=1,2,\dots,MB=1,2,…,M,AAA(包括两端)中任何两个不同出现的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 N1≤l<r≤N和Al=Ar=BA_l=A_r=BAl=Ar=B,都存在一个整数mmm(l≤m≤rl\leq m\leq rl≤m≤r),如Am=XBA_m=X_BAm=XB。
分析:
读题考虑DP,观察数据范围,1≤M≤101\leq M \leq 101≤M≤10,1≤N≤1041\leq N \leq 10^41≤N≤104,猜测本题复杂度可能是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到当前的kkk,xkx_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。
评测机有一个隐藏的正整数RRR和NNN整数A1,A2,…,ANA_1,A_2,\dots,A_NA1,A2,…,AN。保证∣Ai∣≤R|A_i|\le R∣Ai∣≤R和∣∑i=1NAi∣≤R\left|\displaystyle\sum_{i=1}^{N}A_i\right|\le Ri=1∑NAi≤R。
有一块黑板,可以在上面书写绝对值不超过RRR的整数。最初,黑板是空的。
评测机在黑板上依次写下了数值A1,A2,…,ANA_1,A_2,\dots,A_NA1,A2,…,AN。你的任务是经过若干操作后使黑板上只包含一个值∑i=1NAi\displaystyle\sum_{i=1}^{N}A_ii=1∑NAi。
你无法直接知道RRR和AiA_iAi的值,但可以与评测机互动最多250002500025000次。
对于正整数iii,让XiX_iXi成为写在黑板上的第iii个整数。具体来说,Xi=AiX_i=A_iXi=Ai为i=1,2,…,Ni=1,2,\dots,Ni=1,2,…,N。
在一次交互中,可以指定两个不同的正整数iii和jjj并选择以下操作之一:
- 执行加法运算。评测机将擦除黑板上的XiX_iXi和XjX_jXj并在黑板上写下Xi+XjX_i+X_jXi+Xj。
∣Xi+Xj∣≤R|X_i+X_j|\leq R∣Xi+Xj∣≤R必须保持不变。 - 进行比较。评测机会告诉你Xi<XjX_i\lt X_jXi<Xj是真还是假。
在这里,在每次交互开始时,写在黑板上的第iii和第jjj个整数必须没有被擦除。
适当地进行交互操作,以便在所有交互操作之后,黑板上只包含一个值∑i=1NAi\displaystyle\sum_{i=1}^{N}A_ii=1∑NAi。
RRR和AiA_iAi的值是在程序与评测机开始交互之前确定的。
分析:
首先我们想到进行排序,可以发现∣A1+An∣≤R|A_1+A_n|\le R∣A1+An∣≤R一定满足,分情况讨论可证,发现这个性质后,我们可以进一步处理问题。
首先将序列排序,取头尾两数求和,产生一个新的数,将其插入剩余的序列中,由于剩下的序列也是升序的,可以二分插入,由此产生一个新的序列,对这个序列不断重复上述操作即可。这样只需要排序一次,再加上合并需要的次数,总次数为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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1267

被折叠的 条评论
为什么被折叠?



