Codeforces 1312 Div.2 笔记(1)
感想
感觉半夜打比赛很多时候脑子转不过来,看懂了A-E的题面,剩下两道题还需琢磨,C题起床之后就想到做法然而赛场上没有想出来;B题初步猜到结论然而没有敢往上写,只是交上了一个部分分。心态还是要调整,还有做题技巧还要提高。
题目
A Two Regular Polygons
解析
给定一个凸正 n n n边形 p p p,问可不可以在里面内嵌一个正 m m m边形 q q q,使得 q q q的顶点全部都是 p p p的顶点,且 q q q的中心和 p p p的中心重合。
无非就是判断 p p p的顶点去掉 m m 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 a,若对于任何 i ≠ j , i − a i ≠ j − a j i \ne j, i - a_i \ne j - a_j i=j,i−ai=j−aj则称这个数组为好的。给出几组数问如何调整才能调整成好数组。
一开始想到全排列挨个试,虽然最大复杂度 n ! 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 v数组全是0,给定一个数 k k k,你可以有若干操作步骤,第 i i i次将 v v v中的某一元素加上 k i k^i ki,给定 a a a数组,问若干次操作后 v v v可不可以变成 a a a。
初步想法是拆数,将每个 a i a_i ai拆成 k i k^i ki然后判断是否有重复。后来不知为何一直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 n个数的数组 a a a,对于任意整数 a i a_i ai都满足 a i ∈ [ 1 , m ] a_i \in [1, m] ai∈[1,m],求有多少种方法使得数组中存在一个 a i a_i ai,使得 a i a_i ai前面的数严格递增, a i a_i ai后面的数严格递减。
比赛时打眼一看推公式就跳过去了没做。早上起来推了半天没有思路,看别人的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)
i=2∑n−1(n−1m)(i−2n−2)(n−i)
首先,
a
i
a_i
ai不能在第一位和最后一位,所以从第2到n - 1每一位都可以放最大值
a
i
a_i
ai,放上之后左右两边分别可以有
C
n
−
1
m
C^{m}_{n - 1}
Cn−1m和
C
i
−
2
n
−
2
C^{n - 2}_{i - 2}
Ci−2n−2种放数的方法,然后最大值可以有
n
−
i
n - i
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 ai和 a i + 1 a_i + 1 ai+1相同,那么可以把这个两个数替换成一个数 a i + 1 a_i + 1 ai+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;
}