Codeforces 1312 Div.2 笔记(1)

Codeforces 1312 Div.2 笔记(1)

感想

感觉半夜打比赛很多时候脑子转不过来,看懂了A-E的题面,剩下两道题还需琢磨,C题起床之后就想到做法然而赛场上没有想出来;B题初步猜到结论然而没有敢往上写,只是交上了一个部分分。心态还是要调整,还有做题技巧还要提高。

题目

A Two Regular Polygons

解析

给定一个凸正 n n 边形 p p ,问可不可以在里面内嵌一个正 m m 边形 q q ,使得 q q 的顶点全部都是 p p 的顶点,且 q q 的中心和 p p 的中心重合。

无非就是判断 p p 的顶点去掉 m m 个之后剩下的顶点数是否还是 m m 的整数倍,3分钟切。

代码

#include <bits/stdc++.h>

typedef long long ll;

ll nextInt()
{
	ll num = 0;
	char c = 0;
	bool flag = false;
	while ((c = std::getchar()) == ' ' || c == '\r' || c == '\t' || c == '\n');
	if (c == '-')
		flag = true;
	else
		num = c - 48;
	while (std::isdigit(c = std::getchar()))
		num = num * 10 + c - 48;
	return (flag ? -1 : 1) * num;
}

bool ok(const int n, const int m)
{
	if (n < 6)
		return false;
	int t = n - m;
	if (t % m == 0)
		return true;
	return false;
}

int main(int argc, char **argv)
{
	int T = nextInt();
	while (T--)
	{
		int n = nextInt(), m = nextInt();
		if (ok(n, m))
			std::cout << "YES\n";
		else
			std::cout << "NO\n";
	}
}

B Bogosort

解析

给定一个数组 a a ,若对于任何 i j , i a i j a j i \ne j, i - a_i \ne j - a_j 则称这个数组为好的。给出几组数问如何调整才能调整成好数组。

一开始想到全排列挨个试,虽然最大复杂度 n ! n! 但是感觉有机会,但不出所料TLE。后来想到是不是排序后倒序输出就好,但没敢试就结束了。最后查看他人AC代码果然是这样。感觉以后要大胆写。

代码

int main(int argc, char **argv)
{
	int t = nextInt();
	while (t--)
	{
		int n = nextInt();
		for (int i = 1; i <= n; i++)
			a[i] = nextInt();
		std::sort(a + 1, a + n + 1);
		for (int i = n; i >= 1; i--)
			std::cout << a[i] << ' ';
		std::cout << '\n';
	}
	return 0;
}

C Adding Powers

解析

给定 v v 数组全是0,给定一个数 k k ,你可以有若干操作步骤,第 i i 次将 v v 中的某一元素加上 k i k^i ,给定 a a 数组,问若干次操作后 v v 可不可以变成 a a

初步想法是拆数,将每个 a i a_i 拆成 k i k^i 然后判断是否有重复。后来不知为何一直wa。早晨起来一改竟然过了。

代码

#include <bits/stdc++.h>

typedef long long ll;

ll nextInt()
{
	ll num = 0;
	char c = 0;
	bool flag = false;
	while ((c = std::getchar()) == ' ' || c == '\r' || c == '\t' || c == '\n');
	if (c == '-')
		flag = true;
	else
		num = c - 48;
	while (std::isdigit(c = std::getchar()))
		num = num * 10 + c - 48;
	return (flag ? -1 : 1) * num;
}

const size_t _Siz = 1922;

ll a[_Siz];

int main(int argc, char **argv)
{
	int T = nextInt();
	while (T--)
	{
		int n = nextInt(), m = nextInt();
		std::memset(a, 0, sizeof a);
		for (int i = 1; i <= n; i++)
		{
			ll x = nextInt();
			ll pos = 0;
			while (x != 0) // 问题在这里,wa版本这里写的是while (x % m)
			{
				a[++pos] += x % m;
				x /= m;
			}
		}
		bool flag = true;
		for (int i = 1; i <= 100; i++)
			if (a[i] > 1)
			{
				std::cout << "NO\n";
				flag = false;
				break;
			}
		if (flag)
			std::cout << "YES\n";
	}
}

D Count the Arrays

解析

给定 n n 个数的数组 a a ,对于任意整数 a i a_i 都满足 a i [ 1 , m ] a_i \in [1, m] ,求有多少种方法使得数组中存在一个 a i a_i ,使得 a i a_i 前面的数严格递增, a i a_i 后面的数严格递减。

比赛时打眼一看推公式就跳过去了没做。早上起来推了半天没有思路,看别人的AC代码发现是推组合数或者倍增。自己推了一遍,具体思路如下:
i = 2 n 1 ( m n 1 ) ( n 2 i 2 ) ( n i ) \sum _{i = 2}^{n - 1} \binom {m}{n - 1}\binom {n-2}{i-2}(n - i)
首先, a i a_i 不能在第一位和最后一位,所以从第2到n - 1每一位都可以放最大值 a i a_i ,放上之后左右两边分别可以有 C n 1 m C^{m}_{n - 1} C i 2 n 2 C^{n - 2}_{i - 2} 种放数的方法,然后最大值可以有 n i n - i 种情况,所以要乘 n i n - i 。QED.

代码

#include <bits/stdc++.h>

typedef long long ll;

ll nextInt()
{
	ll num = 0;
	char c = 0;
	bool flag = false;
	while ((c = std::getchar()) == ' ' || c == '\r' || c == '\t' || c == '\n');
	if (c == '-')
		flag = true;
	else
		num = c - 48;
	while (std::isdigit(c = std::getchar()))
		num = num * 10 + c - 48;
	return (flag ? -1 : 1) * num;
}

const size_t _Siz = 1992332;

ll kysumi(ll n, ll m, const ll p)
{
	n = (n % p + p) % p;
	ll ans = 1;
	while (m)
	{
		if (m & 1)
			ans = ans * n % p;
		n = n * n % p;
		m >>= 1;
	}
	return ans;
}

ll fac[_Siz] = { 0 };

const ll M = 998244353;

void Fact()
{
	fac[0] = 1;
	fac[1] = 1;
	for (int i = 2; i <= _Siz - 4; i++)
		fac[i] = i * fac[i - 1] % M;
}

ll C(const ll n, const ll m)
{
	return fac[n] * kysumi(fac[n-m] * fac[m] % M, M - 2, M) % M;
}

int main(int argc, char **argv)
{
	Fact();
//	ll T = nextInt();
//	while (T--)
//	{
		ll ans = 0;
		ll n = nextInt(), m = nextInt();
		for (int i = 2; i <= n - 1; i++)
			ans = (ans + C(m, n - 1) * C(n - 2, i - 2) % M * (n - i) % M) % M;
		std::cout << ans << std::endl;
//	}
	return 0;
}

E Array Shrinking

解析

给定一个数组,如果这个数组里有两个数 a i a_i a i + 1 a_i + 1 相同,那么可以把这个两个数替换成一个数 a i + 1 a_i + 1 。问执行若干遍操作之后数组最短是多少。

比赛现场的时候就纯暴力模拟这个过程,不出所料wa了第5个点。感觉是dp,然而没有推出正确的方程。早上起来列了个表之后推出做法了:

用f[i, j]代表起点为i,终点为j的子区间,如果可以合并的话该值为合并后的值,否则为0。枚举长度和左节点i,如果可以合并的话取最小值dp[i]。

代码

#include <bits/stdc++.h>

typedef long long ll;

ll nextInt()
{
	ll num = 0;
	char c = 0;
	bool flag = false;
	while ((c = std::getchar()) == ' ' || c == '\r' || c == '\t' || c == '\n');
	if (c == '-')
		flag = true;
	else
		num = c - 48;
	while (std::isdigit(c = std::getchar()))
		num = num * 10 + c - 48;
	return (flag ? -1 : 1) * num;
}

const size_t _Siz = 622;

ll a[_Siz] = { 0 }, b[_Siz] = { 0 }, n, m;

ll f[_Siz][_Siz], dp[_Siz];

int main(int argc, char **argv)
{
	n = nextInt();
	for (int i = 1; i <= n; i++)
	{
		a[i] = nextInt();
		f[i][i] = a[i];
	}
	for (int len = 2; len <= n; len++)
		for (int l = 1; l <= n - len + 1; l++)
		{
			int r = l + len - 1;
			for (int k = l; k <= r; k++)
			{
				if (f[l][k] && f[k + 1][r] && f[l][k] == f[k + 1][r])
					f[l][r] = f[l][k] + 1;
			}
		}
	std::memset(dp, 0x3f, sizeof dp);
	dp[0] = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j < i; j++)
			if (f[j + 1][i])
				dp[i] = std::min(dp[i], dp[j] + 1);
	std::cout << dp[n] << std::endl;
	return 0;
}
发布了40 篇原创文章 · 获赞 0 · 访问量 5130

猜你喜欢

转载自blog.csdn.net/edward00324258/article/details/104771140