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}\)到数组末尾,反之则使这个功能永久禁用,问遍历过的元素和是多少?

解析:

  1. 可以发现,添加\(x\)\(\frac{a[i]}{x}\)的操作其实遍历了之后,新增元素的和还是\(a[i]\),那么问题就转化为数组中每个元素的值出现过几次,也就是在反复循环遍历这个数组。
  2. 需要求得循环遍历数组的次数,设遇到的第一个不被\(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}} \]

  1. 这样就可以得知数组循环访问了\(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]\)对应的钱代替,每个礼物只能被买一次,求给所有人送达礼物的最小金额(所给礼物序列按金额升序)

解析:

  1. 由于礼物越来越贵,所以编号靠后的人越应该早早拿到编号靠前的礼物,这样就不用必须交靠后的礼物的钱了
  2. 那这样便可以将人的需求编号从大到小遍历,并且定义一个下标代表此时可以送的礼物的最小下标,如果编号大于这个下标,那么就可以送礼物给他,否则因为编号之前的礼物已经被送过了,所以只能给对应的钱

二者相等的情况其实不需要考虑,因为即使选择了购买礼物,下一个人的下标一定是小于等于当前的下标的,所以是肯定用不到 (礼物金额<必交金额)这个贪心条件的

#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\)次查询,问每秒对应的组内元素个数的最大值是多少?

解析:

  1. 由于\(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\)也是一个完全平方数
  2. 如果\(a\times b\)是一个完全平方数,那么对于\(a\)\(b\)来说,他们质因数分解后每个质因子的指数的奇偶性相同,于是可以用质因数分解判断是否属于同一组(用01代表每一个质因数有偶数个或奇数个,哈希编码)
  3. 同时可以发现除第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]);
		}
	}
}
posted @ 2021-01-11 00:51  DreamW1ngs  阅读(45)  评论(0编辑  收藏  举报