比赛链接:https://atcoder.jp/contests/abc336
比赛时间:2024 年 1 月 14 日 20:00-21:40
标签:模拟
题意:给定一个 n n n,输出 L L L、 n n n个 o o o和 n g ng ng。
题解:按题意模拟即可。
代码:
#include
using namespace std;
int main() {
cout << "L";
int n;
cin >> n;
for (int i = 1; i <= n; i++)
cout << "o";
cout << "ng";
return 0;
}
标签:模拟
题意:给定一个十进制数 n n n,求该数转换成 2 2 2进制之后末尾连续 0 0 0的个数。( 1 < = n < = 1 0 9 1<=n<=10^9 1<=n<=109)
题解:模拟,二进制转换,累计一下末尾 0 0 0个数,直到不是 0 0 0,跳出循环。
代码:
#include
using namespace std;
int main() {
int n, c = 0;
cin >> n;
while (n) {
if (n % 2 == 0) c++;
else break;
n /= 2;
}
cout << c;
return 0;
}
标签:思维、数学
题意:给出定义十进制数 x x x的所有数位都是偶数(即 0 、 2 、 4 、 6 、 8 0、2、4、6、8 0、2、4、6、8)的时候称为 “好整数”,给定一个 n n n,求第 n n n小的 “好整数”。( 1 < = n < = 1 0 12 1<=n<=10^{12} 1<=n<=1012)
前几个 “好整数” 分别为 0 、 2 、 4 、 6 、 8 、 20 、 22 、 24 、 26 、 28... 0、2、4、6、8、20、22、24、26、28... 0、2、4、6、8、20、22、24、26、28...
题解:每个数位都是最多 5 5 5种可能,我们可以先求出超过 n n n需要的位数,比如第 8 8 8小的,一位最多只有 5 5 5种,两位能到 25 25 25种,所以至少得两位。
接着我们再求目前当前这位对应的到底是 0 、 2 、 4 、 6 、 8 0、2、4、6、8 0、2、4、6、8中的哪一个。
比如第 8 8 8小的,我们求第一位上的数字的时候,
1 ∗ 5 < 8 1*5<8 1∗5<8
2 ∗ 5 > 8 2*5>8 2∗5>8
所以我们能确定,第一位上的数字是 2 2 2。对于后面的第二位来说,我们需要把刚刚第一位带来的整个部分的个数给减掉, 8 − 5 = 3 8-5=3 8−5=3。
1 ∗ 1 < 3 1*1<3 1∗1<3
1 ∗ 2 < 3 1*2<3 1∗2<3
1 ∗ 3 > = 3 1*3>=3 1∗3>=3
所以第二位上的数字是 4 4 4。
代码:
#include
using namespace std;
int main() {
long long n, k = 1, c = 0;
cin >> n;
while (k <= n) {
k *= 5;
c++;
}
for (int i = 1; i <= c; i++) {
k /= 5;
for (int j = 1; j <= 5; j++) {
if (j * k >= n) {
n -= (j-1) * k;
cout << (j - 1) * 2;
break;
}
}
}
return 0;
}
标签:动态规划、前缀和
题意:金字塔型序列: 1 、 2... k − 1 、 k 、 k − 1...2 、 1 1、2...k-1、k、k-1...2、1 1、2...k−1、k、k−1...2、1。给定一个长度为 n n n的序列 a i a_i ai,可以进行重复性的两种操作:
求能够形成的金字塔型序列的最大长度。( 1 < = n < = 2 ∗ 1 0 5 , 1 < = a i < = 1 0 9 1<=n<=2*10^{5},1<=a_i<=10^9 1<=n<=2∗105,1<=ai<=109)
题解:比较常见的套路,洛谷也有类似的题:P1091 合唱队形
从左往右维护一个 p r e [ i ] pre[i] pre[i]:以 a i a_i ai作为结尾的最长左金字塔序列的长度
从右往左维护一个 s u f [ i ] suf[i] suf[i]:以 a i a_i ai作为结尾的最长右金字塔序列的长度
我们以 p r e [ i ] pre[i] pre[i]为例,分别来观察一下
例子 1 1 1: 1 、 2 、 3 1、2、3 1、2、3
例子 2 2 2: 1 、 2 、 2 1、2、2 1、2、2
例子 3 3 3: 3 、 1 、 2 3、1、2 3、1、2
按照题目中能把数变小和删除前后数字的操作,能推出当前的 p r e [ i ] pre[i] pre[i]要从前面的 p r e [ i − 1 ] pre[i-1] pre[i−1]和当前 a i a_i ai较小的那个推过来:
p r e [ i ] = m i n ( p r e [ i − 1 ] + 1 , a [ i ] ) pre[i] = min(pre[i - 1] + 1, a[i]) pre[i]=min(pre[i−1]+1,a[i])
s u f suf suf同理,最终枚举每个数作为金字塔尖,左边金字塔序列长度和右边金子塔序列长度中取小的那个能够形成的金字塔序列长度,然后维护一个最大值。
代码:
#include
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
ll a[N], pre[N], suf[N];
int main() {
ll n, ans = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
pre[i] = min(pre[i - 1] + 1, a[i]);
}
for (int i = n; i >= 1; i--) {
suf[i] = min(suf[i + 1] + 1, a[i]);
}
for (int i = 1; i <= n; i++) {
ans = max(ans, min(pre[i], suf[i]));
}
cout << ans;
return 0;
}
标签:数位 d p dp dp
题意:给定一个 n n n,求小于等于 n n n的数中有多少个能被自己的位数之和整除。( 1 < = n < = 1 0 14 1<=n<=10^{14} 1<=n<=1014)
**题解:**数位 d p dp dp模版题, d p [ p o s ] [ s u m ] [ m o d ] dp[pos][sum][mod] dp[pos][sum][mod]表示当第 p o s pos pos位各位数字之和为 s u m sum sum除原数的余数是 m o d mod mod且有没超出边界时候的个数。
代码:
#include
using namespace std;
typedef long long ll;
ll n, m, cnt = 0;
// dp[i][j][k]: 第i位 当前数字和为j 取模结果为k的个数
ll a[20], dp[20][10*20][10*20];
ll dfs(ll pos, ll sum, ll mod, ll flag) {
if (sum > m) return 0;
if (pos == 0) return sum == m && mod == 0;
if (!flag && dp[pos][sum][mod] != -1) return dp[pos][sum][mod];
ll x = flag ? a[pos] : 9;
ll ans = 0;
for (ll i = 0; i <= x; i++) {
ans += dfs(pos - 1, sum + i, (mod * 10 + i) % m, flag && (i == a[pos]));
}
if (!flag) dp[pos][sum][mod] = ans;
return ans;
}
int main() {
cin >> n;
while (n) {
a[++cnt] = n % 10;
n /= 10;
}
ll res = 0;
for (int i = 1; i <= 9 * cnt; i++) {
m = i;
memset(dp, -1, sizeof(dp));
res += dfs(cnt, 0, 0, 1);
}
cout << res;
return 0;
}