Codeforces Round #694 (Div. 2)
A(水题)
题目链接
⭐
题目:
给出\(n\)和\(x\),对一个长度为\(n\)的数组\(b\),可以进行若干次合并任意两个元素,问对于最后数组中的剩余元素\(\sum \lceil\frac{b[i]}{x}\rceil\)的最小值和最大值分别是多少?
解析:
- 最小值:很显然,让所有元素全部合并成一个元素,这样就能把向上取整所制造的空缺尽可能地填补
- 最大值:与最小值相反,不填不空缺,即让数组保持原样可以达成目的
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
LL a[100005];
int main()
{
LL r;
int T;
LL n, x;
LL mx, mn;
scanf("%d", &T);
while (T--)
{
mx = 0, mn = 0;
scanf("%lld%lld", &n, &x);
for (int i = 0; i < n; ++i)
scanf("%lld", &a[i]), mn += a[i], mx += (a[i] + x - 1) / x;
printf("%lld %lld\n", (mn + x - 1) / x, mx);
}
}
B(思维)
题目链接
⭐⭐
题目:
给出\(n\)和\(x\),对一个长度为\(n\)的数组\(a\),顺序遍历它的元素,如果遇到的元素可以被\(x\)整除,则添加\(x\)个\(\frac{a[i]}{x}\)到数组末尾,反之则使这个功能永久禁用,问遍历过的元素和是多少?
解析:
- 可以发现,添加\(x\)个\(\frac{a[i]}{x}\)的操作其实遍历了之后,新增元素的和还是\(a[i]\),那么问题就转化为数组中每个元素的值出现过几次,也就是在反复循环遍历这个数组。
- 需要求得循环遍历数组的次数,设遇到的第一个不被\(x\)整除的数的祖先是\(a[q]\),那么他一定是所有元素中拥有最小的\(x^{min}\)的幂次方作为因子
\[a[0],a[1]\dots a[n-1],\underbrace{\frac{a[0]}{x}\dots}_{x},\underbrace{\frac{a[1]}{x}\dots}_{x}\underbrace{\frac{a[0]}{x^{min}}\dots}_{x^{min}}\ ,\dots,\underbrace{\frac{a[q]}{x^{min}}\dots}_{x^{min}}
\]
- 这样就可以得知数组循环访问了\(min\)次,且要注意在原数组中下标在\(q\)之前的数仍然被统计了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
LL dat[100005];
LL sum;
int main()
{
int T;
LL n, x, t;
int mn;
scanf("%d", &T);
while (T--)
{
mn = 0x3f3f3f3f;
sum = 0;
scanf("%lld%lld", &n, &x);
for (int i = 0; i < n; ++i)
{
scanf("%lld", &dat[i]);
sum += dat[i];
int b = 0;
LL j = 1;
while (dat[i] % j == 0)
j *= x, ++b;
if (mn > b)
{
mn = b;
t = i;
}
}
LL ret = sum * mn;
for (int i = 0; i < t; ++i)
ret += dat[i];
printf("%lld\n", ret);
}
}
C (贪心)
题目链接
⭐⭐
题目:
有\(n\)个人,以及\(m\)个礼物,每个人都只能买编号在\(a[i]\)之前的礼物,或者直接给\(a[i]\)对应的钱代替,每个礼物只能被买一次,求给所有人送达礼物的最小金额(所给礼物序列按金额升序)
解析:
- 由于礼物越来越贵,所以编号靠后的人越应该早早拿到编号靠前的礼物,这样就不用必须交靠后的礼物的钱了
- 那这样便可以将人的需求编号从大到小遍历,并且定义一个下标代表此时可以送的礼物的最小下标,如果编号大于这个下标,那么就可以送礼物给他,否则因为编号之前的礼物已经被送过了,所以只能给对应的钱
二者相等的情况其实不需要考虑,因为即使选择了购买礼物,下一个人的下标一定是小于等于当前的下标的,所以是肯定用不到 (礼物金额<必交金额)这个贪心条件的
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
const int maxn = 3e5 + 5;
int k[maxn], c[maxn];
int main()
{
int T;
int n, x, t;
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &n, &x);
for (int i = 0; i < n; ++i)
scanf("%d", &k[i]);
sort(k, k + n);
for (int i = 1; i <= x; ++i)
scanf("%d", &c[i]);
t = 1;
LL ret = 0;
for (int i = n - 1; i >= 0; --i)
if (k[i] > t)
ret += c[t++];
else
ret += c[k[i]];
printf("%lld\n", ret);
}
}
D(公式推导+质因数分解+哈希)
题目链接
⭐⭐⭐⭐
题目:
定义对于任意两个元素满足\(\frac{lcm(a,b)}{gcd(a,b)}=x^2\)那么认为这两个元素是相邻的。现在给出一个数组,将彼此相邻的元素看成一个组,每秒组内各元素都会被组的卷积所代替,那么给出\(q\)次查询,问每秒对应的组内元素个数的最大值是多少?
解析:
- 由于\(lcm(a,b)=\frac{a\times b}{gcd(a,b)}\),所以\(a\times b=(x\times gcd(a,b)^2)\),因此可以得到\(\frac{lcm(a,b)}{gcd(a,b)}=x^2\Leftrightarrow a\times b=y^2\),\(a\times b\)也是一个完全平方数
- 如果\(a\times b\)是一个完全平方数,那么对于\(a\)和\(b\)来说,他们质因数分解后每个质因子的指数的奇偶性相同,于是可以用质因数分解判断是否属于同一组(用01代表每一个质因数有偶数个或奇数个,哈希编码)
- 同时可以发现除第0s外,在以后的元素替换中,如果初始组内元素有偶数个或者质因数的指数均为偶数,那么在以后合成后的元素一定质因数的指数均为偶数,同理如果初始组内元素有奇数各且存在质因数的指数不为偶数,那么会一直保持着这个状态,于是可以得出结论第\(0\)s为一个状态,第\(1\sim\infin\)为另一个状态
注意:1s 的答案记得要在 0s 和 1s 中取最大值
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
/*===========================================*/
const int maxn = 1000;
const int base = 19260817;
bool vis[maxn];
int prime[500];
map<int, int> m;
int T, t, n, q, cnt;
int main()
{
scanf("%d", &T);
for (int i = 2; i < maxn; ++i) {
if (!vis[i]) prime[cnt++] = i;
for (int j = 0; j < cnt && i * prime[j] < maxn; ++j) {
vis[i * prime[j]] = true;
if (i % prime[j] == 0) break;
}
}
while (T--) {
m.clear();
scanf("%d", &n);
while (n--) {
int tt = 0;
scanf("%d", &t);
for (int i = 0; i < cnt && t != 1; ++i) {
if (t % prime[i] == 0) {
int c = 0;
while (t % prime[i] == 0) {
t /= prime[i], ++c;
}
if (c & 1) tt = base * tt + prime[i];
}
}
if (t != 1) tt = base * tt + t;
++m[tt];
}
int ans[2] = { 0 };
for (auto& i : m) {
ans[0] = max(ans[0], i.second);
if (!i.first || i.second % 2 == 0) ans[1] += i.second;
}
ans[1] = max(ans[0], ans[1]);
scanf("%d", &q);
while (q--)
{
scanf("%d", &t);
printf("%d\n", ans[(bool)t]);
}
}
}
努力变成更好的自己吧!