AtCoder Regular Contest


重游程序设计竞赛

AtCoder Regular Contest 168

A - <Inversion>

题意:一个长度为N-1的字符串S(只含有字符< ,> ),构造一个长度为N的序列X,均为整数
X序列相邻两个数字依次满足字符串S的关系,求该序列逆序对数最少值

数据范围:

  • $2 \le N \le 250000$

思路:对于>>>取值依次取值为 x+3>x+2>x+1>x,对于<<<依次取 y<y+1<y+2<y+2
对于x+?的数字,整体依次递增取值,而y+?的数字,整体也依次递增
这样答案只存在于每段>>>:对于每段>>>长度为n,答案为n(n+1)/2
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 1e9 + 7;
constexpr int N = 1e5 + 9;


string s;
int n;
void solve() {
	cin >> n;
	cin >> s;
	s = s + "<";
	int ans = 0;
	for (int i = 0, j = 0; i < n; i++) {
		if (s[i] == '>') {
			ans += j;
		} else {
			ans += j * (j + 1) / 2;
			j = 0;
		}
	}
	cout << ans << "\n";
}

constexpr bool test = 0;
signed main() {
	cin.tie(0)->ios::sync_with_stdio(false);
	cout << fixed << setprecision(20);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

B - Arbitrary Nim

题意:尼姆博奕之类问题,不会┭┮﹏┭┮

数据范围:

  • $2 \le N \le 250000$
  • $1 \le A_{i} \le 10^{9}$

AtCoder Regular Contest 167

A - Toasts for Breakfast Party

题意:N个数$A_{i}$,分成M组,每组最多2个,设每组和为$B_{j}$
求min ${\textstyle \sum_{j=1}^{M}}$ $B_{j}^{2}$

数据范围:

  • $1 \le N \le 2 \times 10^{5}$
  • $\frac{N}{2} \le M \le N$
  • $1 \le A_{i} \le 2 \times 10^{5}$

思路:对数组A递增排序后,依次对前N-M组分2个,后2M-N组分一个
对于前N-M组,设 a ≤ b ≤ c ≤ d,因为$(a+d)^{2}$+$(b+c)^{2}$ ≤ $(a+b)^{2}$+$(c+d)^{2}$
所以将A数组前2(N-M)个,同上形式两两组合
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 1e9 + 7;
constexpr int N = 1e6 + 9;

void solve() {
	int n, m;
	cin >> n >> m;
	vector<int>a(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	sort(a.begin() + 1, a.end());
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		ans += a[i] * a[i];
	}
	int l = 1, r = (n - m) * 2;
	while (l < r) {
		ans += 2 * a[l] * a[r];
		l += 1;
		r -= 1;
	}
	cout << ans << "\n";
}


constexpr bool test = 0;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cout << fixed << setprecision(20);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

B - Product of Divisors

题意:$A^{B}$ 的所有因子乘积可以除以多少次A,答案对998244353取模

数据范围:

  • $2 \le A \le 10^{12}$
  • $0 \le B \le 10^{18}$
  • A,B都为整数

思路:因数是成对出现的,对每个因数$x$都有$x \times \frac{A^{B}}{x}$

设$A^{B}$有K个因子

若K为偶数,把因数两两结合,则$A^{B}$的因数之积是$A^{\frac{BK}{2}}$,答案$\frac{BK}{2}$

若K为奇数,说明$A^{B}$为完全平方数,同理$A^{B}$的因数之积是$A^{\frac{B(K-1)}{2}}\times\sqrt{A^{B}}$,答案也是$\frac{BK}{2}$,但不能直接运算

两者算K是一样的,但在后续运算乘2逆元时有差异,也可理解为向下取整影响答案

补充:$A^{B}$为完全平方数时,A为平方数或B为偶数,乘B时将B * y写成B%mod * y,避免爆数据范围

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 998244353;
constexpr int N = 1e5 + 9;


template<typename T>
T qmi(T n, long long k) {
	T res = 1;
	while (k) {
		if (k & 1) res = res * n;
		n *= n;
		k >>= 1;
	}
	return res;
}
template <const int M>
struct ModInt {
	int x;
	int norm(int x) {
		if (x < 0) {
			x += M;
		}
		if (x >= M) {
			x -= M;
		}
		return x;
	}
	ModInt(int x = 0) : x(norm(x % M)) {}
	int val() const {
		return x;
	}
	ModInt operator -() const {
		return ModInt(norm(M - x));
	}
	ModInt inv() const {
		assert(x != 0);
		return qmi(*this, M - 2);
	}
	ModInt &operator *=(const ModInt &rhs) {
		x = (long long)(x) * rhs.x % M;
		return *this;
	}
	ModInt &operator +=(const ModInt &rhs) {
		x = norm(x + rhs.x);
		return *this;
	}
	ModInt &operator -=(const ModInt &rhs) {
		x = norm(x - rhs.x);
		return *this;
	}
	ModInt &operator /=(const ModInt &rhs) {
		return *this *= rhs.inv();
	}
	bool operator < (const ModInt &rhs) {
		return x < rhs.x;
	}
	bool operator == (const ModInt &rhs) {
		return x == rhs.x;
	}
	bool operator != (const ModInt &rhs) {
		return x != rhs.x;
	}
	friend ModInt operator *(const ModInt &lhs, const ModInt &rhs) {
		ModInt res = lhs;
		res *= rhs;
		return res;
	}
	friend ModInt operator +(const ModInt &lhs, const ModInt &rhs) {
		ModInt res = lhs;
		res += rhs;
		return res;
	}
	friend ModInt operator -(const ModInt &lhs, const ModInt &rhs) {
		ModInt res = lhs;
		res -= rhs;
		return res;
	}
	friend ModInt operator /(const ModInt &lhs, const ModInt &rhs) {
		ModInt res = lhs;
		res /= rhs;
		return res;
	}
	friend std::istream &operator >>(std::istream &is, ModInt &a) {
		long long v;
		is >> v;
		a = ModInt(v);
		return is;
	}
	friend std::ostream &operator <<(std::ostream &os, const ModInt &a) {
		return os << a.val();
	}
};
using Z = ModInt<(int)(998244353)>;
void solve() {
	int a, b;
	cin >> a >> b;
	map<int, int>mp;
	bool flag = 0;
	int t = sqrt(a);
	if (t * t == a) {
		flag = 1;
	}
	for (int i = 2; i * i <= a; i++) {
		while (a % i == 0) {
			mp[i] += 1;
			a /= i;
		}
	}
	if (a > 1) {
		mp[a] += 1;
	}
	Z ans = 1;
	for (auto [x, y] : mp) {
		ans = ans *  (b % mod * y + 1);
	}
	if (b % 2 == 0 || flag) {
		ans -= 1;
		ans /= 2;
		ans *= b;
		ans += b / 2;
	} else {
		ans /= 2;
		ans *= b;
	}
	cout << ans << "\n";
}


constexpr bool test = 0;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cout << fixed << setprecision(20);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

AtCoder Regular Contest 166

A - Replace C or Swap AB

题意:T组样例,每组两个长度相同的字符串X, Y,且只含有A,B,C三种字符
对X有如下三种操作:

  • 选择一个字符C变为A
  • 选择一个字符C变为B
  • 选择字符串AB变为BA

能否从X->Y

数据范围:

  • $1 \le T \le 2 \times 10^{5}$
  • $1 \le N \le 2 \times 10^{5}$
  • $\sum_{i=1}^{T} N_{i} \le 2 \times 10^{5}$

思路:如果$Y_{i}$是C显然不合法,除去Y中字符C的位置,那么字符C位置只能出现在X中。
以字符C的位置分段考虑,AB替换成BA,相当于将字符A向后移动一个位置。可以贪心地将A放到前面,B放后面
得到结论:
每段从前往后判断,截止目前位置X中A, C的数量是否大于等于Y中A的数量
每段从后往前判断,截止目前位置X中B, C的数量是否大于等于Y中B的数量
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 1e9 + 7;
constexpr int N = 1e5 + 9;


void solve() {
	int n;
	cin >> n;
	string x, y;
	cin >> x >> y;
	x = "#" + x;
	y = "#" + y;
	for (int i = 1; i <= n; i++) {
		if (x[i] != 'C' && y[i] == 'C') {
			cout << "No\n";
			return ;
		}
	}
	auto check = [&](int l, int r)->bool{
		if (l > r) {
			return true;
		}
		int cnta = 0, cntb = 0;
		for (int i = l; i <= r; i++) {
			if (x[i] == 'A' || x[i] == 'C') {
				cnta += 1;
			}
			if (y[i] == 'A') {
				cntb += 1;
			}
			if (cnta < cntb) {
				return false;
			}
		}
		cnta = 0, cntb = 0;
		for (int i = r; i >= l; i--) {
			if (x[i] == 'B' || x[i] == 'C') {
				cnta += 1;
			}
			if (y[i] == 'B') {
				cntb += 1;
			}
			if (cnta < cntb) {
				return false;
			}
		}
		return true;
	};
	int last = 1;
	for (int i = 1; i <= n; i++) {
		if (y[i] == 'C') {
			if (check(last, i - 1)) {
				last = i + 1;
			} else {
				cout << "No\n";
				return ;
			}
		}
	}
	if (check(last, n)) {
		cout << "Yes\n";
	} else {
		cout << "No\n";
	}
}

constexpr bool test = 1;
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cout << fixed << setprecision(10);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

B - Make Multiples

题意:给定长度为N的序列A,三个数值a,b,c
每次可以操作$A_{i}$->$A_{i}$ + 1
问最少可以操作多少次,使序列A中有a的倍数,b的倍数,c的倍数

数据范围:

  • $1 \le N \le 2 \times 10^{5}$
  • $1 \le a,b,c \le 10^{6}$
  • $1 \le A_{i} \le 10^{18}$

思路:有以下几种情况
3个数分别对应一个数
2个数对应一个数,剩下一个数对应一个数
3个数对应一个数
$根据x成为y的倍数,所需最小操作次数为(y-x\bmod y)\bmod y$

状压dp代码(暴力代码太丑不发了):

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 1e9 + 7;
constexpr int N = 1e5 + 9;


int lcm(int a, int b) {
	return a / __gcd(a, b) * b;
}
void solve() {
	int n;
	cin >> n;
	vector<int>a(3);
	for (int i = 0; i < 3; i++) {
		cin >> a[i];
	}
	vector<int>x(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> x[i];
	}
	vector<int>y(8, 1);
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 3; j++) {
			if (i >> j & 1) {
				y[i] = lcm(y[i], a[j]);
			}
		}
	}
	vector<vector<int>>f(n + 1, vector<int>(8, INF));
	f[0][0] = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < 8; j++) {
			for (int k = 0; k < 8; k++) {
				if ((j & k) == 0) {
					f[i][j | k] = min(f[i][j | k], f[i - 1][k] + (y[j] - x[i] % y[j]) % y[j]);
				}
			}
		}
	}
	cout << f[n][7] << "\n";
}

constexpr bool test = 0;
signed main() {
	cin.tie(0)->ios::sync_with_stdio(false);
	cout << fixed << setprecision(10);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

AtCoder Regular Contest 165

A - Sum equals LCM

题意:给定一个整数N,是否可以表示为
$A_{1}+A_{2}+···+A_{n}=N$
$A_{1},A_{2},···,A_{n}小公倍数为N$
$n \ge 2$

数据范围:

  • $1\le T\le 100$
  • $1\le N\le 10^{9}$

思路:当$N={p}^{e}(1\le e)$时$A_{i}$ 最大为 ${p}^{k}(k<e)$ ,显然最大公约数为 ${A}^{k}$ 不满足题意
当N不为上述情况时,$N\ge 6$即N具有至少两个不同质因数p,q
令$k=N-\frac{N}{p}-\frac{N}{q}\ge N-\frac{N}{2}-\frac{N}{3}=\frac{N}{6}>0$
我们有$(A_{1},A_{2},···,A_{k},A_{k+1},A_{k+2})=(\underbrace{1,1,···,1}_{k},\frac{N}{p},\frac{N}{q})$
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 1e9 + 7;
constexpr int N = 2e5 + 9;


void solve() {
	int n;
	cin >> n;
	int sum = 0;
	for (int i = 2; i * i <= n; i++) {
		if (n % i == 0) {
			sum += 1;
			while (n % i == 0) {
				n /= i;
			}
		}
	}
	if (n > 1) {
		sum += 1;
	}
	cout << (sum > 1 ? "Yes" : "No") << "\n";
}

constexpr bool test = 1;
signed main() {
	cin.tie(0)->ios::sync_with_stdio(false);
	cout << fixed << setprecision(20);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

B - Sliding Window Sort 2

题意:给定长度为n的排列P和一个整数K
可进行一次操作,选择一个长度为K的区间,对区间进行升序排列,求最大的字典序排列

数据范围:

  • $1\le K \le N \le 2 \times 10^{5}$

思路:首先无论如何,排序之后一定会使字典序变小或不变,则如果存在一个长度为K的递增区间
我们一定操作此区间,否则将操作尽可能往后放,即区间[n-k+1,n]
然后向前寻找前一个位置的值是否大于左端点,若大于说明向前移动一个位置后,字典序比移动前更小
所以直接操作移动前的区间,若向前寻找前一个位置的值小于左端点,因为[n-k]往前找满足左边值递增
且[n-k]右边最小值大于啊[n-k]才能往前找,若小于[n-k]则排序之后比之前更小,显然此区间不能取
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
constexpr int mod = 1e9 + 7;
constexpr int N = 2e5 + 9;


void solve() {
	int n, k;
	cin >> n >> k;
	vector<int>a(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	int t = 1;
	for (int i = 1; i < n; i++) {
		if (a[i] < a[i + 1]) {
			t += 1;
			if (t == k) {
				for (int i = 1; i <= n; i++) {
					cout << a[i] << " \n"[i == n];
				}
				return ;
			}
		} else {
			t = 1;
		}
	}
	vector<int>f(n + 1);
	f[n - k] = INF;
	for (int i = n - k + 1; i <= n; i++) {
		f[i] = min(f[i - 1], a[i]);
	}
	int x = n - k + 1;
	for (int i = n - k; i >= 1; i--) {
		if (a[i] < a[i + 1] && f[i + k - 1] > a[n - k]) {
			x = i;
		}
		if (a[i] > a[i + 1]) {
			break;
		}
	}
	sort(a.begin() + x, a.begin() + x + k);
	for (int i = 1; i <= n; i++) {
		cout << a[i] << " \n"[i == n];
	}
}

constexpr bool test = 1;
signed main() {
	cin.tie(0)->ios::sync_with_stdio(false);
	cout << fixed << setprecision(20);
	int T = 1;
	if (test) cin >> T;
	for (int i = 1; i <= T; i++) solve();
	return 0;
}

文章作者: FW
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 FW !
评论
  目录