【比赛】Codeforces Round #698 (Div. 2) 题解

前言

之前是想着用博客去记录自己的比赛记录,但是打了几场比赛之后发现没有什么提升,感觉是方法的问题,所以现在换种方法,将每一次比赛的题目搞懂搞透,让比赛补的每一题都具有意义!


比赛链接

点击此处进入


A. Nezzar and Colorful Balls

【题目大意】 给定n个求球上有分别有n个不递减数,要求给求上色,使得同一种颜色的球上面的数字是严格单调递增的,求最少的颜色数量。
【分析】 最少有多少种不同的颜色也就是数组中能至少拆分成多少严格单调递增的组,那么再转化一下就是求数组中的众数的个数。

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 100 + 10;
int num[N];

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	int T;
	cin >> T;
	while (T--)
	{
		memset(num, 0, sizeof num);
		int n;
		cin >> n;
		_for (i, 0, n)
		{
			int x;
			cin >> x;
			num[x]++;
		}

		int ans = -1;
		_for(i, 0, N) ans = max(ans, num[i]);
		cout << ans << endl;
	}
	
	return 0;
}

B. Nezzar and Lucky Number

【题目大意】 数字d是 1 − 9 1-9 19中的一个数,求一个数是否存在几个包含d的数字总和等于x的情况。
【分析】 10 ∗ d − 10 ∗ d + 9 10 * d - 10 * d + 9 10d10d+9 中的数是一定包含d的,所以说,如果x是大于10 * d的,可以一直将他减d,直到减到这个范围,那么也就是说, x > = 10 ∗ d x >= 10 * d x>=10d必然是存在符合题意的数的。
对于小于的情况就可以直接暴力枚举判断了,经过几次尝试发现能符合题意的数字都有会存在 i ∗ 10 + j ∗ d = = x i * 10 + j * d == x i10+jd==x的情况,i 最大为d,大于d *10 的情况不需要枚举了, j 最小取1(必须要有一个d), 最大取10,同样的道理。

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 100 + 10;
int q, d;

bool check(int n)
{
	_rep(i, 0, d) _rep(j, 1, 10) if (i * 10 + j * d == n) return true;
	return false;
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	int T;
	cin >> T;
	while (T--)
	{
		cin >> q >> d;
		_for (i, 0, q)
		{
			int x; cin >> x;
			if (x >= d * 10 || check(x)) puts("YES");
			else puts("NO");
		}
	}
	
	return 0;
}

C. Nezzar and Symmetric Array

【题目大意】 数组a是由n 对不同的相反数组成的2 * n个数。
数组d的每一个数符合 d i = ∑ j = 1 2 n ∣ a i − a j ∣ d_i = \sum_{j = 1}^{2n}\lvert a_i - a_j\rvert di=j=12naiaj 题目给你d数组,判断是否存在组成d数组的数组a。

【分析】 通过公式可以看出来,d中的数也是成对出现的,而且是每对中的两个数是相同的。
那么现在给下标重新分配定义域 1 ≤ i ≤ n 1 \leq i \leq n 1in 并且将d排序那么就会有

  • d 2 i − 1 = d 2 i d_{2i-1} = d_{2i} d2i1=d2i
  • d 2 i ≠ d 2 i + 2 d_{2i} \neq d_{2i + 2} d2i=d2i+2
  • d 2 i d_{2i} d2i 一定是 由i 或者i + n组成的

有这点限制条件害不够,继续推
我们对 ∣ a i − a j ∣ \lvert a_i - a_j \rvert aiaj式子进行变形可以得到 ( a i + a j ) + ( a i − a j ) = 2 a j ( 1 ≤ i < j ≤ n ) (a_i + a_j) + (a_i - a_j) = 2a_j (1 \leq i < j \leq n) (ai+aj)+(aiaj)=2aj1i<jn ,这个怎么理解呢,画个数轴, a i a_i ai在中间 ± a j \pm a_j ±aj在两边,即可得到 2 a j 2a_j 2aj

还需要与数组d联系起来,所以的话,最后需要推一个连接的公式:
d 2 n − d 2 n − 2 = ∑ i = 1 n ( a n + a i ) + ∑ i = 1 n ( a n − a i ) − ∑ i = 1 n ∣ a n − 1 − a i ∣ − ∑ i = 1 n ( a n − 1 + a i ) = ( 2 n − 2 ) ( a n − a n − 1 ) d_{2n}−d_{2n−2}=\sum_{i=1}^n(a_n + a_i) + \sum_{i=1}^n(a_n − a_i) - \sum_{i=1}^n\lvert a_{n - 1} - a_i\rvert - \sum_{i=1}^n(a_{n - 1} + a_i) = (2n - 2)(a_n - a_{n - 1}) d2nd2n2=i=1n(an+ai)+i=1n(anai)i=1nan1aii=1n(an1+ai)=(2n2)(anan1)

通过n = 3 为例:
d 1 = 2 a 1 + 2 a 2 + 2 a 3 d_1 = 2a_1 + 2a_2 + 2a_3 d1=2a1+2a2+2a3
d 2 = 4 a 2 + 2 a 3 d_2 = 4a_2 + 2a_3 d2=4a2+2a3
d 3 = 6 a 3 d_3 = 6a_3 d3=6a3
那么就可以通过刚刚那个公式从 d 1 d_1 d1推到 d 3 = 6 a 3 d_3 = 6a_3 d3=6a3
反推验证 判断能否整除 2 n 2n 2n即可。

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define FOR(i, a, b) for (int i = (a); i >= (b); --i)
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 200000 + 10, INF = 0x3f3f3f3f;
ll n, a[N], c[N], d[N];

bool slove()
{
	_rep(i, 1, n)
	{
		if (a[2 * i - 1] != a[2 * i]) return  false;
		d[i] = a[2 * i];
	}
	_rep(i, 2, n)
	{
		if (d[i] == d[i - 1] || (d[i] - d[i - 1]) % (2 * i - 2)) return false;
		c[i] = (d[i] - d[i - 1]) / (2 * i - 2);
	}
	_rep(i, 1, n - 1) d[1] -= 2 * c[i + 1] * (n - i) ;
	return d[1] > 0 && d[1] % (2 * n) == 0;
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	int T;
	cin >> T;
	while(T--)
	{
		cin >> n;
		_rep(i, 1, 2 * n) cin >> a[i];
		sort(a + 1, a + 2 * n + 1);
		if (slove()) puts("YES");
		else puts("NO");
		
	}
	
	return 0;
}

D. Nezzar and Board

【题目大意】 给定n个写在板子上的数,从板子上挑选两个数x、y然后将 2 x − y 2x - y 2xy 写在板子上,问板子上有没有可能会出现k。

【分析】 首先一看数据范围就不简单,应该往数学方向。然后分析我们将要写在板子上的数, 我们可以将他拆分为 : x + ( x − y ) x + (x - y) x+(xy) 把括号里面的数看成一个整体,那么就可以表示为板子上任意两个数的差,也就是他们之间的线性组合,即 a i + ∑ j , k ( a j − a k ) a_i + \sum_{j, k}(a_j − a_k) ai+j,k(ajak),判断式子有无解,这时候我们就要想到裴蜀定理( x 、 y x 、 y xy 的最大公约数 d d d,那么 a x + b y = d ax + by = d ax+by=d一定有解的情况。)
那么我们进一步把公式写出来: x 1 ∗ p 1 + x 2 ∗ p 2 + x 3 ∗ p 3 + … … + x n ∗ p n = k − x i x_1 * p_1 + x_2 * p_2 + x_3 * p_3 + …… + x_n * p_n = k - x_i x1p1+x2p2+x3p3++xnpn=kxi
这个 x i x_i xi可以在数组中任意取一个值,因为无论取哪一个值,都可以由其他的线性组合出来,所以公式变为:
x 1 ∗ p 1 + x 2 ∗ p 2 + x 3 ∗ p 3 + … … + x n ∗ p n = k − x 1 x_1 * p_1 + x_2 * p_2 + x_3 * p_3 + …… + x_n * p_n = k - x_1 x1p1+x2p2+x3p3++xnpn=kx1 那么只要 g ∣ ( k − x 1 ) g | (k - x_1) g(kx1),必然就有解。
g为两两之间的差,这里我们不需要全部弄出来,只需要依次减去最前面的即可,因为都可以线性组合出来。

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define FOR(i, a, b) for (int i = (a); i >= (b); --i)
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 200000 + 10, INF = 0x3f3f3f3f;
ll n, k, a[N];

ll gcd(ll a, ll b)
{
	return b ? gcd(b, a % b) : a;
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> k;
		_for(i, 0, n) cin >> a[i];
		ll g = 0;
		_for(i, 1, n) g = gcd(a[i] - a[0], g);
		if ((k - a[0]) % g) puts("NO");
		else puts("YES");
	}
	
	return 0;
}

E - Nezzar and Binary String

【题目大意】 给定长度为n的两个二进制串s、f, q个区间,每个区间中可以修改严格小于一半的元素,判断s是否能通过改变区间中的元素达到q。

【分析】 这个题目要用逆向思维去想,打个比方,由河的起点去找某一个支流的端点是非常困难的,但是从端点找起点又是非常简单的。
所以我们就反向操作 f ,每个区间只取占主导地位的数字,最后判断能否到达s即可。

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 200000 + 10, INF = 0x3f3f3f3f;
int L[N], R[N], n, q;
char s[N], f[N];

struct Node
{
	int l, r, lz;
	ll v;
}tr[N << 2];

void push_up(int u)
{
	tr[u].v = tr[u << 1].v + tr[u << 1 | 1].v;
}

void push_down(int t)
{
	Node& u = tr[t];
	if (~u.lz)
	{
		Node& ls = tr[t << 1], & rs = tr[t << 1 | 1];
		ls.v = (ls.r - ls.l + 1) * u.lz;
		ls.lz = u.lz;
		rs.v = (rs.r - rs.l + 1) * u.lz;
		rs.lz = u.lz;
		u.lz = -1;
	}
}

void build(int u, int l, int r)
{
	if (l == r) tr[u] = { l, r, -1, f[l] - '0'};
	else
	{
		int mid = l + r >> 1;
		tr[u] = { l, r, -1, 0 };
		build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
		push_up(u);
	}
}

void modify(int t, int l, int r, int k)
{
	Node& u = tr[t];
	if (l <= u.l && u.r <= r) {
		u.v = k * (u.r - u.l + 1);
		u.lz = k;
		return;
	}
	push_down(t);
	int mid = u.l + u.r >> 1;
	if (l <= mid) modify(t << 1, l, r, k);
	if (r > mid) modify(t << 1 | 1, l, r, k);
	push_up(t);
}

ll query(int t, int l, int r)
{
	Node& u = tr[t];
	if (l <= u.l && u.r <= r) return u.v;
	push_down(t);
	ll ans = 0;
	int mid = u.l + u.r >> 1;
	if (l <= mid) ans = query(t << 1, l, r);
	if (r > mid) ans += query(t << 1 | 1, l, r);
	return ans;
}

bool solve()
{
	For(i, q - 1, 0)
	{
		int a = query(1, L[i], R[i]), b = R[i] - L[i] + 1 - a;
		if (a == b) return false;
		modify(1, L[i], R[i], a > b);
	}
	_rep(i, 1, n) if (s[i] - '0' != query(1, i, i)) return false;
	return true;
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cout.tie(0);
	cin.tie(0);

	int T;
	cin >> T;
	while (T--)
	{
		cin >> n >> q;
		cin >> s  + 1 >> f + 1;
		build(1, 1, n);
		_for(i, 0, q) cin >> L[i] >> R[i];
		
		if (solve()) puts("YES");
		else puts("NO");
	}
	
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值