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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章