牛客周赛141
A 回文(version 1)
知识点:判断回文字符串
思路:判断是否为完全平方数,再判断这个数和他的平方根是否为回文即可
注意:不要读错题,不是这个数和他的平方,而是这个数和他的平方根
未知(version 1)
知识点:异或性质
思路:一个数异或他自己为 \(0\),所以异或 \(y\) 就可以了
回文(version 2)
知识点:模拟,
思路:把 \(nn\) 全换成 \(m\) 就行了
未知(version 2)
知识点:枚举
思路:如果有两个甚至多个 \(1\),或者有一个 \(1\) 和两个相同的数,就已经满足条件了。如果没有这些条件,注意到,\(a_i\) 的最大值也才 \(1e9\),就是底数是 \(2\),指数也最高到达 \(30\) ,所以 \(a_j\) 的范围为 \(2-30\) ,而且相同的数再次遍历是没有意义的,所以直接遍历一次就可以,时间复杂度很小,先对原数组进行排序,这样如果超出范围了直接 \(break\) 就行了。
点击查看代码
void solve()
{
cin >> n;
vi a(n);
int c1 = 0;
bool ok = 0;
for (int i = 0; i < n; i++)
{
cin >> a[i];
if (a[i] == 1)
c1++;
}
sort(all(a));
for (int i = 1; i < n; i++)
{
if (a[i] == a[i - 1] && a[i] != 1)
ok = 1;
}
if (c1 >= 2 || (c1 == 1 && ok))
{
cout << "YES" << endl;
return;
}
for (int i = 0; i < n; i++)
{
if (a[i] == 1 || (i > 0 && a[i] == a[i - 1]))
continue;
for (int j = 0; j < n; j++)
{
if (a[j] == 1 || (j > 0 && a[j] == a[j - 1]))
continue;
if (a[j] >= 30)
break;
int num = 1;
for (int k = 0; k < a[j]; k++)
{
num *= a[i];
}
if (num > 1000000000LL)
break;
if (binary_search(a.begin(), a.end(), num))
{
cout << "YES" << endl;
return;
}
}
}
cout << "NO" << endl;
}
未知(version 3)
知识点:构造
思路:先考虑构造成功的必要条件,对于 \(m\) 级来说,至少需要 \(m+1\) 个点来构造,剩下的 \(m-1\) 级,至少需要一个点来构造,最多需要 \(i\) 个点来构造, 所以 \(n>2*m\),且 \(n<m*(m+1)/2+1\),如果点的数量合法,那我们开始构造。
先构造出主链,对于下一个层级,计算这个层级最多容纳的点的数量 ,应该是 \(min(i,剩下的所有可支配点)\),找出这条链在主链上的起点进行连接即可
Code
点击查看代码
void solve()
{
cin >> n >> m;
int ceng = m;
int idx = 2;
if (n < 2 * m || n > 1 + m * (m + 1) / 2)
{
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
for (int i = 1; i <= m; i++)
{
cout << i << ' ' << idx << endl;
idx++;
}
ceng--;
while (ceng >= 1)
{
int res = n - idx + 1;
int mx = res - ceng;
int t = min(mx, ceng - 1);
int cnt = 1 + t;
int chu = (ceng - cnt) + 1;
int pre = chu;
while (cnt--)
{
cout << pre << ' ' << idx << endl;
pre = idx;
idx++;
}
ceng--;
}
}
回文(version 3)
知识点:前缀和,二次前缀和
思路:如果 \(x=1\),结果为区间长度,如果 \(x=2\),结果为区间内每个字符里面选两个即可,这两种情况比较好处理 ,考虑当 \(x=3\) 的时候,先考虑暴力做法,中间那个字符是不需要考虑的,所以遍历字符串,处理每一个字符,利用乘法原理将两边字符的数量相乘即可,数量由前缀和就可以得出,但这样的时间复杂度太高,所以对计算的数学式子进行化简,遍历字符串,当遍历到第 \(i\) 个字符时,对于左右两边每个字符来说,有贡献 \((p_{i-1}-p_{l-1})*(p_r-p_i)\),拆解得到,\((p_{i-1}*p_r)+(p_{l-r}*p_i)+(-p_{l-1}*p_r)+(-p_i*p_{i-1})\) 共四项,对于第四项,可以预处理然后用前缀和计算,对于第三项,可以直接用前缀和得到,对于前两项,发现均有一项是固定的,变得是 \(i\),所以预处理他们的和然后相乘即可,他们的和是在对原前缀和的基础上进行二次前缀和。
Code
点击查看代码
int f[M][26];
int s1[M][26];
int a[M][26];
int s3[M][26];
void solve()
{
int q;
cin >> n >> q;
cin >> s;
s = '3' + s;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 26; j++)
{
f[i][j] = f[i - 1][j];
}
f[i][s[i] - 'a']++;
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 26; j++)
{
s1[i][j] = s1[i - 1][j];
s1[i][j] += f[i][j];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 26; j++)
{
s3[i][j] = s3[i - 1][j];
a[i][j] = -f[i - 1][j] * f[i][j];
s3[i][j] += a[i][j];
}
}
while (q--)
{
cin >> l >> r >> x;
if (x == 1)
{
cout << r - l + 1 << endl;
}
else if (x == 2)
{
int res = 0;
for (int i = 0; i < 26; i++)
{
int sum = f[r][i] - f[l - 1][i];
res += sum * (sum - 1) / 2;
}
cout << res << endl;
}
else
{
int res = 0;
for (int i = 0; i < 26; i++)
{
res += s3[r][i] - s3[l - 1][i];
res += (r - l + 1) * (-f[l - 1][i] * f[r][i]);
int d = (l >= 2) ? s1[l - 2][i] : 0;
res += f[r][i] * (s1[r - 1][i] - d);
res += f[l - 1][i] * (s1[r][i] - s1[l - 1][i]);
}
cout << res << endl;
}
}
}

浙公网安备 33010602011771号