ABC336 A-E

明天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);
	vectorans;
	--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;
	vectora;
	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);
}

写的挺抽象的,应该还有更好的做法

你可能感兴趣的:(算法)