Codeforces Round 863 (Div. 3)

Codeforces Round 863 (Div. 3)

CF传送门

Solution

A. Insert Digit

找到第一个比 \(d\) 小的数位,插到它前面即可。

B. Conveyor Belts

关键在于:给出坐标 \((x,y)\),如何确定该点在第几层?

首先把坐标换算成以中心为原点的坐标(见下),答案即为两个点的层数之差的绝对值。

//将(x1,y1)换算成以中心为原点的坐标(xa,ya)
int xa = y1 <= n/2 ? y1 - n / 2 - 1 : y1 - n / 2;
int ya = x1 <= n/2 ? x1 - n / 2 - 1 : x1 - n / 2;

C. Restore the Array 【构造】

注意到:

\(b[i]>b[i+1]\),则 \(b[i]\) 不能是从 \(a[i+1]\) 得到的,而应该是从 \(a[i]\) 得来,即:\(a[i]=b[i],a[i+1]\leq b[i+1]<b[i]\)

\(b[i]<b[i+1]\),则 \(b[i+1]\) 不能是从 \(a[i+1]\) 的得到的,而应该是从 \(a[i+2]\) 得来,即:\(a[i+2]=b[i+1],a[i+1]\leq b[i]<b[i+1]\)

用一个数组标记这些限制,然后直接构造就行。

D. Umka and a Long Flight 【减治】

关键:抓住\(Fibonacci\)的定义 \(F_n=F_{n-2}+F_{n-1}\),即 \(F_{n-2}=F_{n}-F_{n-1}\)

先不考虑 \(the\ painted\ cell\),直接将边长为 \(F_n\)\(F_{n+1}\) 的矩形划分成 \(n+1\) 个正方形,且至多只能有一对正方形变成相等。结合 \(Fib\) 的定义,可以得到一个划分方法是:首先分出以 \(F_n\) 为边长的正方形,剩下的矩形边长为 \(F_{n}, F_{n+1}-F_n=F_{n-1}\),再分以 \(F_{n-1}\) 为边长的,知道最后分出两个 \(1*1\) 的正方形。

即: \(F_{n}*F_{n+1}=F_n^2+F_{n-1}^2+...+F_1^2+F_0^2\),这个式子可以通过直接展开来证:\(F_n*F_{n+1}=F_n*(F_n+F_{n-1})=F_n^2+F_n*F_{n-1}=...=F_n^2+F_{n-1}^2+...+F_1^2+F_0^2\)。由于所有的正方形边长都必须是 \(Fibonacci\) 中的数且至多只能由两个相等的,所有只能由这一种划分方法,替换任意一个都会超出原矩形的大小。

现在将 \(the\ painted\ cell\) 考虑进来,它只能作为边长为 \(1\) 的正方形。考虑每次先将 \(F_n\) 的正方形分出来,若分不出则无解;若能分出来则递归解决更小规模的问题。

E. Living Sequence 【减治】

逐位确定。

初始化两个数组:\(n0[]\)\(n1[]\)\(n0[i]\) 表示位数为 \(i\),可包含前导零的合法数字总数;\(n1[i]\)为不含前导零的。

易得:\(n0[i]=n0[i-1]*9,n1[i]=n0[i-1]*8\)

对于给定的 \(k\),先找到比它大的最小的 \(n1[digi]\) 确定答案的位数为 \(digi\)。接着递归按位求答案。

感觉这也可以理解为减治。

G1. Vlad and the Nice Paths (easy version) 【DP】

\(dp[i][j]\) 表示在前 \(i\) 个数中,选出了 \(j\) 块的方案数。

顺序枚举 \(i\)\(j\)\(dp[i][j]\) 有两个转移来源:一个是 \(dp[i-1][j]\),一个是将第 \(i\) 个数作为最后一个块中的数并计算答案。对于第二种情况,倒序枚举一个 \(t\),并且记录已经遇到了多少个与 \(c[i]\) 相等的数 \(num\),当 \(num\geq k\)\(c[t]=c[i]\) 时,累计答案\(dp[i][j]+=dp[t-1][j-1]*C_{num-2}^{k-2}\)。(相当于已知最后一个块的开头和结尾,只有这样才能准确转移)。

复杂度为 \(O(n^3)\)

G2. Vlad and the Nice Paths (hard version)

数据范围扩大,\(O(n^3)\) 的方法不再适用。根据题目暗示,需要优化成 \(O(n^2)\) 的。

容易发现,我们只需要记录最长的路径和其方案数,就可以得到最后的答案。

Code

A

#include<iostream>
using namespace std;
string s;
int n;
char c;
int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> n >> c;
		cin >> s;
		int len = s.length();
		int pos = len;
		for (int i = 0;i < len;++i)
			if (s[i] < c) {
				pos = i - 1;
				break;
			}
		if (pos == -1) {
			cout << c << s << endl;
			continue;
		}
		else
			cout << s.substr(0, pos + 1) << c;
		if (pos != len)
			cout << s.substr(pos + 1, len - pos - 1) << endl;
		else
			cout << endl;
	}
	return 0;
}

B

#include<iostream>
#include<cmath>
using namespace std;
int main() {
	int T;
	cin >> T;
	while (T--) {
		int n, x1, x2, y1, y2;
		cin >> n >> x1 >> y1 >> x2 >> y2;
		int xa = y1 <= n/2 ? y1 - n / 2 - 1 : y1 - n / 2;
		int ya = x1 <= n/2 ? x1 - n / 2 - 1 : x1 - n / 2;
		int xb = y2 <= n/2 ? y2 - n / 2 - 1 : y2 - n / 2;
		int yb = x2 <= n/2 ? x2 - n / 2 - 1 : x2 - n / 2;
		int r1 = max(abs(xa), abs(ya));
		int r2 = max(abs(xb), abs(yb));
		cout << abs(r1 - r2) << endl;
	}
	return 0;
}

C

#include<iostream>
using namespace std;
const int N = 2e5 + 5;
int n, b[N], a[N];
int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> n;
		--n;
		for (int i = 1;i <= n;++i) {
			cin >> b[i];
			a[i] = -3; 
		}

		a[n + 1] = b[n];
		a[1] = b[1];

		for (int i = 2;i <= n;++i) {
			if (b[i] > b[i - 1]) {
				a[i + 1] = b[i];
				a[i] = -1; //标记不能取 b[i]
			}
			else if (b[i] < b[i - 1]) {
				a[i] = -2; //标记不能放 b[i-1]
			}
		}
		for (int i = 2;i <= n;++i) {
			if (a[i] == -3)
				a[i] = b[i];
			else if (a[i] == -1)
				a[i] = b[i - 1];
			else
				a[i] = b[i];
		}

		for (int i = 1;i <= n + 1;++i)
			cout << a[i] << " ";
		cout << endl;

	}
	return 0;
}

D

#include<iostream>
#define ll long long
using namespace std;
const int N = 50;
ll fib[N];
void calc_fib() {
	fib[0] = fib[1] = 1;
	for (int i = 2;i < N;++i)
		fib[i] = fib[i - 1] + fib[i - 2];
}
bool sol(int n, int x, int y) {
	if (n == 1) return 1;
	if (y > fib[n])
		return sol(n - 1, y - fib[n], x);
	if (y <= fib[n + 1] - fib[n])
		return sol(n - 1, y, x);
	return 0;
}
int main() {
	calc_fib();
	int T;cin >> T;
	while (T--) {
		int n, x, y;
		cin >> n >> x >> y;
		if (sol(n, x, y)) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

E

#include<iostream>
#define ll long long
using namespace std;
const int N = 20;
ll n1[N], n0[N];
ll k;
string ans;
void init_num() {
	n1[0] = n0[0] = 1;
	for (int i = 1;i <= 14;++i) {
		n1[i] = 8 * n0[i - 1];
		n0[i] = 9 * n0[i - 1];
	}
}
void sol(int digi, ll cur, int tag) {
	if (digi == 0) return;
	int sele = 0;
	int tot = tag == 1 ? n1[digi] : n0[digi];
	for (int i = tag;i <= 9;++i) {
		if (i == 4) continue;
		if ((i - int(i > 4)) * n0[digi - 1] <= cur)
			sele = i;
		else
			break;
	}
	if (tag == 1 && sele == 0) sol(digi - 1, cur - (sele - int(sele > 4)) * n0[digi - 1], 1); //估计求位数的时候有点小错所以这里还加一个tag判断,但是懒得去改了ww
	else {
		ans.push_back('0' + sele);
		sol(digi - 1, cur - (sele - int(sele > 4)) * n0[digi - 1], 0);
	}
}
int main() {
	init_num();
	int T;cin >> T;
	while (T--) {
		cin >> k;
		int digi = 1;
		while (n1[digi] < k) ++digi;
		ans.clear();
		sol(digi, k, 1);
		cout << ans << endl;
	}
	return 0;
}

G1

#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int N = 105, mod = 1e9 + 7;
int n, k, c[N], fac[N];
ll dp[N][N];   //dp[i][j]: 在前 i 个中,构造出 j 块的方案数
void init_fac() {
	fac[0] = 1;
	for (int i = 1;i < N;++i) fac[i] = 1ll * fac[i - 1] * i % mod;
}
int inv(int x) {
	int y = mod - 2;
	int res = 1;
	while (y) {
		if (y & 1)
			res = 1ll * res * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1;
	}
	return res;
}
int C(int x, int y) {
	//cout << x << " " << y << endl;
	return 1ll * fac[x] * inv(fac[y]) % mod * inv(fac[x - y]) % mod;
}
int main() {
	init_fac();
	int T;cin >> T;
	while (T--) {
		cin >> n >> k;
		for (int i = 1;i <= n;++i) cin >> c[i];

		memset(dp, 0, sizeof(dp));
		dp[0][0] = 1;
		for (int i = 1;i <= n;++i)
			for (int j = 0;j <= n / k;++j) {
				dp[i][j] += dp[i - 1][j];
				if (j == 0) continue;
				int num = 1;
				for (int t = i - 1;t > 0;--t) {
					if (c[t] == c[i]) {
						++num;
						if (num >= k) {
							dp[i][j] += 1ll * dp[t - 1][j - 1] * C(num - 2, k - 2) % mod;
							dp[i][j] %= mod;
						}
					}
				}
				//cout << "dp[" << i << "][" << j << "]=" << dp[i][j] << endl;
			}
		for (int j = n / k;j >= 0;--j) 
			if (dp[n][j] > 0) {
				cout << dp[n][j] << endl;
				break;
			}
	}
	return 0;
}

G2

#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int N = 5005, mod = 1e9 + 7;
int n, k, c[N], fac[N];
ll dp[N][2];   //dp[i][j]: 在前 i 个中,构造出 j 块的方案数
void init_fac() {
	fac[0] = 1;
	for (int i = 1;i < N;++i) fac[i] = 1ll * fac[i - 1] * i % mod;
}
int inv(int x) { //求逆元
	int y = mod - 2;
	int res = 1;
	while (y) {
		if (y & 1)
			res = 1ll * res * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1;
	}
	return res;
}
int C(int x, int y) {
	if (x < 0) return 1;   //注意k=1的特殊情况
	return 1ll * fac[x] * inv(fac[y]) % mod * inv(fac[x - y]) % mod;
}
int main() {
	init_fac();
	int T;cin >> T;
	while (T--) {
		cin >> n >> k;
		for (int i = 1;i <= n;++i) cin >> c[i];

		memset(dp, 0, sizeof(dp));
		dp[0][1] = 1;  //dp[][0]: 最多的块数  dp[][1]: 最多块数对应的方案数
		for (int i = 1;i <= n;++i){
				int num = 0;
				for (int t = i;t > 0;--t) {
					if (c[t] == c[i]) {
						++num;
						if (num >= k) {
							dp[i][0] = max(dp[i][0], dp[t - 1][0] + 1);
							if (dp[t - 1][0] < dp[i][0] - 1) break;
							dp[i][1] += 1ll * dp[t - 1][1] * C(num - 2, k - 2) % mod;
							dp[i][1] %= mod;
						}
					}
				}
				if (dp[i][0] < dp[i - 1][0])
					dp[i][0] = dp[i - 1][0], dp[i][1] = 0;
				if (dp[i][0] == dp[i - 1][0])
					dp[i][1] += dp[i - 1][1], dp[i][1] %= mod;
			}
		cout << dp[n][1] << endl;
	}
	return 0;
}
posted @ 2023-04-07 17:20  DTTTTTTT-  阅读(88)  评论(0)    收藏  举报