Codeforces Round 863 (Div. 3)
Codeforces Round 863 (Div. 3)
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;
}