Codeforces 1312 Div.2 笔记(1)

Codeforces 1312 Div.2 笔记(1)

感想

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

题目

A Two Regular Polygons

解析

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

无非就是判断pp的顶点去掉mm个之后剩下的顶点数是否还是mm的整数倍,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

解析

给定一个数组aa,若对于任何ij,iaijaji \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

解析

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

初步想法是拆数,将每个aia_i拆成kik^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

解析

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

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

解析

给定一个数组,如果这个数组里有两个数aia_iai+1a_i + 1相同,那么可以把这个两个数替换成一个数ai+1a_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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章