文章目录
牛客小白月赛110(打表、容斥+二分、区间dp、DFS)
A. 智乃办赛
思路
首先, n − 1 n-1 n−1 ,把 [ 1 , 500 ] 、 [ 501 , 1000 ] . . . [1, 500]、[501, 1000] ... [1,500]、[501,1000]... 映射到 [ 0 , 499 ] 、 [ 500 , 999 ] . . . [0, 499]、[500, 999] ... [0,499]、[500,999]...
字母 = ( n − 1 ) / 500 + ′ A ′ (n-1) / 500 + 'A' (n−1)/500+′A′。 ( n − 1 ) / 500 (n-1) / 500 (n−1)/500 得到第几段; + ′ A ’ + 'A’ +′A’ 得到对应字母的 a s c l l ascll ascll。
数字 = ( n − 1 ) % 500 + 1 (n-1) \% 500 + 1 (n−1)%500+1。 % 500 \%500 %500 得到余数,即在段内是第几个; + 1 + 1 +1 映射到原区间。
code
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int x = (n-1) / 500;
int y = (n-1) % 500 + 1;
printf("%c%03d\n", x+'A', y); // %03d 三位整型,不足补0
return 0;
}
B. 智乃的Wordle
思路
先统计每个字符出现的情况,再根据题意判断即可。
code
#include<bits/stdc++.h>
using namespace std;
int v[500];
int main(){
string s, t;
cin >> s >> t;
for(int i = 0; i < 8; i++) v[s[i]]++;
for(int i = 0; i < 8; i++){
if(s[i] == t[i]) cout << "g";
else if(v[t[i]] != 0) cout << "y";
else cout << "r";
}
cout << endl;
if(s == t) cout << "congratulations" << endl;
else cout << "defeat" << endl;
return 0;
}
C. 智乃的数字(打表、容斥+二分)
思路
看数据范围,1e5 次查询,每次查询 k 在 1e9。即,在O(log) 或O(1) 的时间复杂度内解决每次查询。
综上,大概率有公式或规律。
打表可以发现,每隔 7 个元素,+30。
求出前7个元素,判断是第几个循环节即可。
完毕。
更深入的思考:
- 奇数,即,不是 2 的倍数
- 以 5 结尾,即,是 5 的倍数,不是 2 的倍数
- 数位和是 3 的倍数,即,是 3 的倍数
三个条件互相有交叉,考虑容斥原理,如下图所示:
(图片来自官方题解)
对于1 到 x,阴影部分满足题意的元素个数为: x / 5 + x / 3 − x / 15 − x / 6 − x / 10 + x / 30 x / 5 + x / 3 - x / 15 - x / 6 - x / 10 + x / 30 x/5+x/3−x/15−x/6−x/10+x/30
理解为计算阴影部分的面积,x / 5 表示 5 的倍数所在圆的面积,x / 3 表示 3 的倍数所在圆的面积,x / 15 表示 5 的倍数与 3 的倍数相交的部分面积,x / 6表示 2 的倍数与 3 的倍数相交的部分面积…
显然,对于区间[1, x],x 越大,满足题意的元素个数越多。
二分找到最小的x,使得区间[1, x]中,有 k 个满足题意的元素。
code
打表:
#include<bits/stdc++.h>
using namespace std;
long long a[1005];
bool check(int x){
if(x % 2 == 0) return false;
if(x % 5 == 0) return true;
int sum = 0;
while(x) sum += x % 10, x /= 10;
return sum % 3 == 0;
}
int main(){
int cnt = 0;
for(int i = 1; i <= 1000; i++){
if(check(i)){
a[cnt++] = i;
// cout << a[cnt] << " "; // 打表用
}
}
int n;
cin >> n;
for(int i = 1; i <= n; i++){
long long x;
cin >> x;
x--;
cout << a[x%7] + x / 7 * 30 << endl;
}
return 0;
}
二分:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 5;
ll count(ll x){
return x / 5 + x / 3 - x / 15 - x / 6 - x / 10 + x / 30;
}
int main(){
int ncase;
cin >> ncase;
while(ncase--){
ll k;
cin >> k;
ll l = 1, r = 1e18, res;
while(l <= r){
ll mid = l + r >> 1;
if(count(mid) >= k){
res = mid;
r = mid - 1;
}
else l = mid + 1;
}
cout << res << endl;
}
return 0;
}
D. 智乃与长短期主义者博弈(区间dp or 记忆化DFS)
思路
根据题意:
- 短期主义者的行为表示为:对于区间 [ l , r ] [l, r] [l,r],每次选取 m a x ( a [ l ] , a [ r ] ) max(a[l], a[r]) max(a[l],a[r])
- 长期主义者的行为表示为:设 h ( l , r ) h(l, r)