牛客周赛 Round 108(思维、位运算、DP、SOSDP)
文章目录
- 牛客周赛 Round 108
- A. ICPC World Finals
- B. 小苯的数字排序
- C. 小苯的数字合并(思维)
- D. 小苯的子序列权值(思维、位运算)
- E. 小苯的有趣数(DP)
- F. AND VS MEX (SOSDP)
牛客周赛 Round 108
A. ICPC World Finals
# Python
s, s1, s2, s3 = map(int, input().split())if s < 425 and (s1 < 60 or s2 < 60 or s3 < 60):print("YES")
else:print("NO")
// C++
#include<bits/stdc++.h>using namespace std;int main(){int s, a, b, c;cin >> s >> a >> b >> c;if(s < 425 && (a < 60 || b < 60 || c < 60)) cout << "YES" << endl;else cout << "NO" << endl;return 0;
}
B. 小苯的数字排序
排序后分奇偶输出即可。
# Python
T = int(input())for _ in range(T):n = int(input())a = [x for x in map(int, input().split())]a = sorted(a)for x in a:if x % 2 == 0:print(x, end=" ")for x in a:if x % 2 != 0:print(x, end=" ")print()
// C++
#include<bits/stdc++.h>using namespace std;int main(){int ncase;cin >> ncase;while(ncase--){int n;cin >> n;vector<int> a;for(int i = 1, x; i <= n; i++){cin >> x;a.push_back(x);}sort(a.begin(), a.end());for(auto x : a) if(x % 2 == 0) cout << x << " ";for(auto x : a) if(x % 2 == 1) cout << x << " ";cout << endl;}return 0;
}
C. 小苯的数字合并(思维)
思路:
假定从左到右依次处理每个元素对答案的贡献。
在处理到第 i 个元素时,由 [1,i−1][1, i-1][1,i−1] 构成的“本质不同的数组“的个数为 XXX,aia_iai 有两个选择:
- 与 [1,i−1][1, i-1][1,i−1] 构成的 “本质不同的数组“ 的最右边的元素合并,得到 XXX 个“本质不同的数组“
- 不与 [1,i−1][1, i-1][1,i−1] 构成的 “本质不同的数组“ 的最右边的元素合并,以单独的形式放在最右边,得到 XXX 个“本质不同的数组“
综上,在第 iii 个元素时,“本质不同的数组“ 个数为 2X2X2X。
综上,对于长度为 nnn 的数组,通过合并得到的“本质不同的数组“ 个数为 2n−12^{n-1}2n−1
为什么是这样?
注意数据范围,ai>=1a_i >= 1ai>=1,but,只有出现负数或者零的时候,才会出现通过合并出现本质相同的数组。
a1a_1a1
a1,a2a_1, a_2a1,a2
a1,a2,a3a_1, a_2, a_3a1,a2,a3
a1,a2+a3a_1, a_2 + a_3a1,a2+a3
a1a_1a1
a1+a2a_1 + a_2a1+a2
a1+a2,a3a_1 + a_2,a_3a1+a2,a3
a1+a2+a3a_1 + a_2 + a_3a1+a2+a3
显然,如果a1,a2,a3>=1a_1,a_2,a_3 >= 1a1,a2,a3>=1时,上述所有序列都是本质不同的。
code:
# Python
T = int(input())for _ in range(T):n = int(input())a = [x for x in map(int, input().split())]res = 1for i in range(n-1):res = res * 2 % 998244353print(res)
// C++
#include<bits/stdc++.h>
#define ll long long
#define mod 998244353using namespace std;ll a[5005];int main(){int ncase;cin >> ncase;while(ncase--){int n;cin >> n;for(int i = 1; i <= n; i++) cin >> a[i];ll res = 1;for(int i = 1; i < n; i++) res = res * 2 % mod;cout << res << endl;}return 0;
}
D. 小苯的子序列权值(思维、位运算)
思路:
首先,权值为偶数的子序列个数 = 全部子序列的个数 - 权值为奇数的子序列个数
全部子序列的个数 = Cn1+Cn2+...+CnnC_n^1 + C_n^2 + ... + C_n^nCn1+Cn2+...+Cnn = 2n−1−12^{n-1} - 12n−1−1
-
奇数 & 偶数 = 偶数
-
奇数 & 奇数 = 奇数
由上可知,权值为奇数的子序列中的所有元素都是奇数。
so,权值为奇数的子序列个数 = CX1+CX2+...+CXXC_X^1 + C_X^2 + ... + C_X^XCX1+CX2+...+CXX = 2X−1−12^{X-1} - 12X−1−1。( XXX 表示全集中奇数的个数)
res=2n−1−1−(2X−1−1)=2(n−1)−2X−1res = 2^{n-1} - 1 - (2^{X-1} - 1) = 2^{(n-1)} - 2^{X-1}res=2n−1−1−(2X−1−1)=2(n−1)−2X−1
code:
// C++
#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
using namespace std;ll a[200005];int main(){int ncase;cin >> ncase;while(ncase--){int n;cin >> n;for(int i = 1; i <= n; i++) cin >> a[i];ll cnt = 0;for(int i = 1; i <= n; i++) cnt += (a[i] % 2);ll res = 1, num = 1;for(int i = 1; i <= n; i++) res = res * 2 % mod;for(int i = 1; i <= cnt; i++) num = num * 2 % mod;res = (res - num + mod) % mod;cout << res << endl;}return 0;
}
E. 小苯的有趣数(DP)
思路:
111 是「有趣的」数字,那么序列一定可以变形为 1、1、1、...、1、sum(a)−(n−1)1、1、1、 ...、1、sum(a) - (n-1)1、1、1、...、1、sum(a)−(n−1)。
故而,答案最小为 n−1n-1n−1。
故而,只需要判断答案是否为 nnn 即可。
综上,问题可以转化为,sum(a)sum(a)sum(a) 是否可以拆分为 nnn 个「有趣的」数字
n<=100,ai<=200n <= 100, a_i <= 200n<=100,ai<=200,则 sum(a)<=2000sum(a) <= 2000sum(a)<=2000。
打表可以发现,小于200020002000的「有趣的」数字只有212121个。
Z={1,4,9,36,81,100,121,144,169,196,225,324,400,441,484,529,900,961,1521,1681}Z = \{1, 4, 9, 36, 81, 100, 121, 144, 169, 196, 225, 324, 400, 441, 484, 529, 900, 961, 1521, 1681\}Z={1,4,9,36,81,100,121,144,169,196,225,324,400,441,484,529,900,961,1521,1681} and 000
设 f[x][y]f[x][y]f[x][y] 表示把 yyy 拆成 xxx 个数是否可行。
-
初始 f[1][y]=1f[1][y] = 1f[1][y]=1,y∈Zy \in Zy∈Z
-
f[x][y+z]∣=f[x−1][y],x∈zf[x][y+z] |= f[x-1][y], x \in zf[x][y+z]∣=f[x−1][y],x∈z
code:
// C++
#include<bits/stdc++.h>using namespace std;int p[100] = {1, 4, 9, 36, 81, 100, 121, 144, 169, 196, 225, 324, 400, 441, 484, 529, 900, 961, 1521, 1681};int f[101][2005];int main() {for(int i = 1; i <= 20; i++) f[1][p[i-1]] = 1;for(int i = 2; i <= 100; i++){for(int j = 19; j >= 0; j--){for(int z = 2000-p[j]; z > 0; z--){f[i][z+p[j]] |= f[i-1][z];}}}int ncase;cin >> ncase;while(ncase--){int n;cin >> n;int sum = 0, x = 0;for(int i = 1; i <= n; i++){cin >> x;sum += x;}cout << (f[n][sum] ? n : n-1) << endl;}return 0;
}
F. AND VS MEX (SOSDP)
思路:
参考出题人的讲解,不再做介绍。传送门
code:
#include<bits/stdc++.h>using namespace std;const int maxn = 1e6 + 5;int f[maxn];int main(){int ncase;cin >> ncase;while(ncase--){int n;cin >> n;for(int i = 0; i <= n; i++) f[i] = (1LL << 31) - 1; // 初始化for(int i = 1, x; i <= n; i++){cin >> x;f[x] = x; // 初始化}for(int i = n; i >= 0; i--){for(int j = 0; j <= 20; j++){int x = i | (1LL << j);if(x > n) break;f[i] &= f[x];}}int res = n + 1;for(int i = 1; i <= n; i++){if(f[i] > i){res = i;break;}}cout << res << endl;}return 0;
}