明天F补完题就加上()
放假了又能网瘾漏w
上周的abc太简单了这周的好难,E出了个数位dp想法以为是想复杂了结果还真是...
Tasks - AtCoder Beginner Contest 336
A - Long Loong
题意:
输出龙的拼音,把'o'换成连续的n个'o'
代码:
void solve()
{int n;scanf("%d", &n);printf("L");while (n--)printf("o");printf("ng\n");
}
B - CTZ
题意:
输出n的二进制表示末尾有多少个连续的0
代码:
直接bitset偷懒了
void solve()
{int x;scanf("%d", &x);bitset<40>st = x;for (int i = 0;; ++i){if (st[i] != 0){printf("%d", i);return;}}
}
C - Even Digits
题意:
输出仅由偶数构成的十进制数中第k小的数
题解:
看成五进制即可,记得特判n=1的情况
void solve()
{LL n;scanf("%lld", &n);vector<int>ans;--n;if (n == 0){printf("0");return;}while (n){ans.push_back(n % 5);n /= 5;}while (ans.size()){printf("%d", ans.back() * 2);ans.pop_back();}
}
D - Pyramid
题意:
给出一个长度为n的正整数数组,你可以进行以下两种操作:
1:将一个元素-1
2:在数组开头或者末尾删除一个元素
使得数组最终变成1,2,3,...,k-1,k,k-1,...,3,2,1的形式,求可能的最大的k
题解:
把每个ai想象成高度,找最大的k相当于是在这个图里找一个最大的金字塔形的东西,例如样例1:
22311 121#
### -> #
##### ###
l[i]表示点i在左斜边上时i位置的最大高度,转移为l[i] = min(l[i - 1] + 1, a[i]),r[i]同理,答案为max({ min(l[i], r[i]) }),具体做法见代码
int a[N], l[N], r[N];
void solve()
{int n, ans = 0;scanf("%d", &n);for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);for (int i = 1, j = n; i <= n; ++i, --j){l[i] = min(l[i - 1] + 1, a[i]);r[j] = min(r[j + 1] + 1, a[j]);}for (int i = 1; i <= n; ++i)ans = max(ans, min(l[i], r[i]));printf("%d\n", ans);
}
E - Digit Sum Divisible
题意:
一个数的数位和为他的十进制表示上的所有数的和,当一个数能被他的数位和整除我们成这个数是好的数,求从0到n一共有多少个好的数
题解:
先枚举所有可能的数位和,对于每个数位和m我们做一个dp求数位和为m且能被m整除的小于等于n的数的数量:
设dp[i][j][k]为对于十进制数的前i位,数位和为j,该数当前的值取模m后的余数为k时的方案数,对于每个确定的dp[i][j][k],它转移到下一位时,设下一位填入的数为x,转移为:dp[i - 1][j + x][(k + x * pow(10, i - 1) % m] += dp[i][j][k],关于限制当前数小于等于n,我们只需要在求完每一位数之后把超过n的部分减去即可。大致思路是这样,具体做法见代码
题解中的数位和我在代码中用的是剩余的数位和,思路是一样的就是和题解中的转移j要反一下
LL pw[20], dp[16][140][140];
void solve()
{LL n, ans = 0;scanf("%lld", &n);LL t = n, mx = 0;vector<int>a;while (t)mx += t % 10, a.push_back(t % 10), t /= 10;mx = max(mx, a.back() - 1 + ((LL)a.size() - 1) * 9);pw[0] = 1;for (int i = 1; i < 20; ++i)pw[i] = pw[i - 1] * 10;for (int m = 1; m <= mx; ++m)//枚举数位和m{LL s = m, md = 0;//按n的每一位选数时的数位和和余数memset(dp, 0, sizeof dp);dp[a.size()][m][0] = 1;for (int i = a.size(); i > 0; --i)//第i位,准备填第i-1位{for (int j = 0; j <= m; ++j)//填数前数位和还剩j{for (int k = 0; k < m; ++k)//余数是k{for (int x = 0; x <= 9 && j - x >= 0; ++x)//第i-1位填x的转移dp[i - 1][j - x][(k + x * pw[i - 1]) % m] += dp[i][j][k];}}//减去超过上界的for (int x = a[i - 1] + 1; x <= 9 && s - x >= 0; ++x){int j = s, k = md;dp[i - 1][j - x][(k + x * pw[i - 1]) % m] -= 1;}s -= a[i - 1], md = (md + a[i - 1] * pw[i - 1]) % m;}ans += dp[0][0][0];}printf("%lld\n", ans);
}
写的挺抽象的,应该还有更好的做法