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=1∑iXj(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 0≤i,j≤N,若 ( Y i < K (Y_i\lt K (Yi<K和 Y j ≥ K ) Y_j\ge K) Yj≥K),则 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 1≤l<r≤N和 A l = A r = B A_l=A_r=B Al=Ar=B,都存在一个整数 m m m( l ≤ m ≤ r l\leq m\leq r l≤m≤r),如 A m = X B A_m=X_B Am=XB。
分析:
读题考虑DP,观察数据范围, 1 ≤ M ≤ 10 1\leq M \leq 10 1≤M≤10, 1 ≤ N ≤ 1 0 4 1\leq N \leq 10^4 1≤N≤104,猜测本题复杂度可能是 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 ∣Ai∣≤R和 ∣ ∑ i = 1 N A i ∣ ≤ R \left|\displaystyle\sum_{i=1}^{N}A_i\right|\le R i=1∑NAi ≤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=1∑NAi。
你无法直接知道 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+Xj∣≤R必须保持不变。 - 进行比较。评测机会告诉你 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=1∑NAi。
R R R和 A i A_i Ai的值是在程序与评测机开始交互之前确定的。
分析:
首先我们想到进行排序,可以发现 ∣ A 1 + A n ∣ ≤ R |A_1+A_n|\le R ∣A1+An∣≤R一定满足,分情况讨论可证,发现这个性质后,我们可以进一步处理问题。
首先将序列排序,取头尾两数求和,产生一个新的数,将其插入剩余的序列中,由于剩下的序列也是升序的,可以二分插入,由此产生一个新的序列,对这个序列不断重复上述操作即可。这样只需要排序一次,再加上合并需要的次数,总次数为 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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。