2023大厂真题提交网址(含题解):
www.CodeFun2000.com(http://101.43.147.120/)
最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
目录
- 2023大厂真题提交网址(含题解):
-
-
- 1.介绍:
- 2.应用:
-
- 2.1.ARC100E
- 2.2:CF1208
- 2.3:SPECIAL PAIRS
- 2.4:E. Compatible Numbers
- 2.5:D. Jzzhu and Numbers(容斥)
- 2.6:HackRank-Subset(动态维护,暴力,均摊分治)
- 2.7:Jersey Number:sosdp+容斥+字符串
- 2.8:K. Pepsi Cola(容斥)
- 2.9:Covering Sets CodeChef - COVERING(贡献计数+容斥)
- 2.10:Uchiha Brothers and Two Products:巧妙转移+容斥
- 2.11:Beautiful Sandwich(难题,待补)
-
1.介绍:
1.1.SOSdp( S u m o v e r s u b s e t s d p Sum \ over \ subsets\ dp Sum over subsets dp)顾名思义,子集和dp。用来维护子集信息的。俗称高维前缀和.
1.2.最常见的形式:求解 d p ( m a s k ) = ∑ i ∈ m a s k a i dp(mask)=\sum_{i \in mask}a_i dp(mask)=∑i∈maskai
注意: i ∈ m a s k 等价于 i & m a s k = i 等价于 i ∣ m a s k = m a s k i \in mask 等价于 i\&mask=i 等价于i|mask=mask i∈mask等价于i&mask=i等价于i∣mask=mask
常见的解法是 O ( 3 n ) O(3^n) O(3n)枚举子集。使用sosdp可以加速到 O ( n ∗ 2 n ) O(n*2^n) O(n∗2n).
1.3.做法: S ( m a s k , i ) S(mask,i) S(mask,i)代表集合 m a s k mask mask只有前 i i i位不同的子集 a i a_i ai和.
合法性:
无非就是证明计数不重不漏呗。对于任意一个 i i i的子集 j j j.它们一定有一段前缀是相同的。所以 i i i的子集的计数可以分解成 l o g i logi logi个阶段去做: i i i的全子集 = 前 l o g i logi logi个位不同的子集和=上面的转移。(这个就有点像前缀和优化了)
1.4.编码:
//iterative version
for(int mask = 0; mask < (1<<N); ++mask){
dp[mask][-1] = A[mask]; //handle base case separately (leaf states)
for(int i = 0;i < N; ++i){
if(mask & (1<<i))
dp[mask][i] = dp[mask][i-1] + dp[mask^(1<<i)][i-1];
else
dp[mask][i] = dp[mask][i-1];
}
F[mask] = dp[mask][N-1];
}
//memory optimized, super easy to code.
for(int i = 0; i<(1<<N); ++i)
F[i] = A[i];
for(int i = 0;i < N; ++i) for(int mask = 0; mask < (1<<N); ++mask){
if(mask & (1<<i))
F[mask] += F[mask^(1<<i)];
}
解释:两种不同的dp顺序。第一种是正常的状压dp的顺序:从小集合往大集合dp。
第二种是从低位到高位去dp。
2.应用:
基础定义比较好理解,但是它的应用非常广泛,技巧多。需要刷比较多的题才能加深对 s o s d p sosdp sosdp的理解。
2.1.ARC100E
题目大意:
给你 2 n 2^n 2n个数。问你每个 k ∈ [ 0 , 2 n − 1 ] k \in[0,2^n-1] k∈[0,2n−1],求解 max i o r j ≤ k { a i + a j } \max_{i\ or\ j\leq k}\{a_i+a_j\} maxi or j≤k{
ai+aj}.
n ≤ 18 n\leq18 n≤18
题目思路:
这个题需要比较奇妙的转换:
1. i ∣ j ≤ k i|j\leq k i∣j≤k比较难求解,将其变成 i ∣ j = g ≤ k i|j=g \leq k i∣j=g≤k,所以每个 i ∣ j = g i|j=g i∣j=g,求前缀最大值即可。
2.但是上面还是不好处理。我们直接转换成子集的形式: ( i ∣ j ) ∈ k (i | j) \in k (i∣j)∈k.那么也就是 i ∈ k & & j ∈ k i \in k \&\& j \in k i∈k&&j∈k.那么显然对每个 k k k,求其下标 i i i为 k k k的子集的最大值和次大值.最后前缀最大值即可.
代码:
pii a[maxn];
void trans (int i , int j)
{
int mx = max (a[i].fi , a[j].fi);
int se = max(min(a[i].fi , a[j].fi) , max (a[i].se , a[j].se));
a[i].fi = mx;
a[i].se = se;
}
int main()
{
ios::sync_with_stdio(false);
int n; cin >> n;
int s = 1 << n;
for (int i = 0 ; i < s ; i++){
int x; cin >> x;
a[i] = {
x , -inf};
}
for (int j = 0 ; j < n ; j++){
for (int i = 0 ; i < s ; i++){
if (i >> j & 1) trans(i , i ^ (1 << j));
}
}
int ans = -inf;
for (int i = 1 ; i < s ; i++){
ans = max(ans , a[i].fi + a[i].se);
cout << ans << endl;
}
return 0;
}
2.2:CF1208
题目大意:
给一个长度为 n n n的序列,求 m a x { a i ∣ ( a j & a k ) } , 1 ≤ i < j < k ≤ n max\{a_i|(a_j\&a_k)\},1 \leq i <j<k \leq n max{
ai∣(aj&ak)},1≤i<j<k≤n
n ≤ 1 e 6 n \leq 1e6