【赛后小结】Codeforces Round #630 (Div. 2)
比赛相关信息
比赛信息
比赛名称: Codeforces Round #630 (Div. 2)
比赛地址: Codeforce
部分题解与小结
B - Composite Coloring
小评
数论题,看了好久想了好多方法,都感觉不太对。后期看完 \(C\) ,感觉这题简单一点,就猜了一下规律码了一发,结果过了……补题发现这是一道极其数论的题目,要到达正确思路只有从规律出发推导,或者瞎猜,总之是非常魔幻的一道题。
题意
给出 \(N(1\le N\le 10^3)\) 个合数,且保证 \(4\le A_i\le10^3\) ,你至多可以将这些数分为 \(11\) 个组,使得:
- 组内所有数字两两不互质(即 \(\tt gcd > 1\) );
多组样例,保证 \(\sum N \le 10^4\) 。输出任意一种分组方式。
思路
我们知道数学规律:
一个合数 \(X\) 的最小质因数小于等于 \(\sqrt X\) 。
由题中给出的范围,我们得到可能的最小质因数 \(\sqrt {1000} \ge 33\) ,而 \(33\) 以内的质数恰好只有 \(11\) 个,所以至此,原题条件转化为:组内所有数字最小质因数相等,得解。
本场比赛代码写的较丑,主要看思路orz。
AC代码
点击查看代码
#define int LL
const int N = 1e6 + 7;
int a[N], ans[N];
void Solve() {
int n; cin >> n; VI mp[105];
FOR (i, 1, n) {
cin >> a[i];
for (int j = 2; j * j <= a[i]; ++ j) {
if (a[i] % j == 0) {
mp[j].pb(a[i]);
break;
}
}
}
int cnt = 0;
FOR (i, 1, 104) {
if (!mp[i].empty()) {
++ cnt;
for (auto it : mp[i]) ans[it] = cnt;
}
}
cout << cnt << endl;
for (int i = 1; i <= n; ++ i) cout << ans[a[i]] << " ";
cout << endl;
}
C - K-Complete Word
小评
初见以为是一道很难的字符串题,由于自己对于字符串的理解不深,所以尝试找规律,稍加尝试结果又过了……赛后补题才后知后觉,虽然自己在书写代码时没有进行严格的证明,但却奇迹般的符合题意,可以说是相当巧妙了。
题意
给出一条长度为 \(N\) 的字符串 \(S\) ,给出周期 \(T\) ,规定操作如下:
- 选择 \(S\) 中的任意位置,将这一位修改成任意字母;
输出最少的操作次数,使得操作后的 \(S\) 是周期为 \(T\) 的回文字符串。多组样例,满足 \(\sum N \le 2 * 10^ 5\) 。
思路
根据题意,我们发现,只需要修改 \(S\) 的前 \(K\) 位,使得每一位与其周期位、对称位均相等,即可使得整个字符串满足题意。
关于这一点的另一个解释是(来自官方题解):满足题意的字符串同样满足——对于每一个周期,其也是回文字符串。
而另一点观察是,通过以上的方式修改,每个位置至多只会被修改一次,故不会发生前后修改矛盾的情况(例如,第 \(i\) 位在这一轮被修改了,在下一轮不会再被修改回去),即我们的每一次修改都是必要的、有效的。所以我们有思路:对于前 \(K\) 位,找到其与其周期位、对称位里出现次数最多的字母 \(x\) ,并将这些位置全部修改为 \(x\) 。
AC代码
点击查看代码
#define int LL
string s;
int n, k, num;
void Solve() {
cin >> n >> k >> s;
int ans = 0;
FOR (i, 0, k - 1) {
int num = 0, a[26] = {};
for (int t = i; t <= n - 1; t += k) { //找到所有需要修改的位置
++ a[s[t] - 'a'];
++ num;
if ((n - t - 1) % k != i) {
++ a[s[n - t - 1] - 'a'];
++ num;
}
}
int m = 0, mm = 0;
FOR (j, 0, 25) { //找到出现次数最多的字母
if (a[j] > m) {
m = a[j];
mm = j;
}
}
ans += num - m;
char x = 'a' + mm;
for (int t = i; t <= n - 1; t += k) {
s[t] = s[n - t - 1] = x; //全部进行修改
}
}
cout << ans << endl;
}
D - Walk on Matrix
小评
虽然题目又是 \(\tt DP\) 又是位运算的,但是这道题本质上并不是很难,大胆的寻找规律即可。
题意
给出一个 \(N*M(1\le N,M\le 500)\) 的矩阵,每一个格子上都有一个数字 \(A_{i,j}(0 \le A_{i, j} \le 3 * 10^5)\) 。现在要从左上角 \((1,1)\) 移动到右下角 \((N,M)\) ,规定移动操作如下:
- 只能向下或向右移动;
- 每达到一个格子,玩家的分数 \(Score\) 会被更新为 \(Score \& A_{i,j}\) ;
为了使得到达 \((N,M)\) 时自己的分数达到最大,Bob使用 \(\tt DP\) 思想设计了一个程序,如下:
然而,这个程序并不正确,现在,给出一个数字 \(K(0\le K \le 10^5)\) ,你需要构建一个合乎规定的矩阵,使得Bob的程序与正确答案恰好相差 \(K\) 。
思路
观察Bob的程序可以得知,其错误的原因在于上一步的最大值并不一定是最优的,即对于位运算 \(\&\) ,局部最优不能构成全局最优。
第一步:假设。矩阵的大小在开始时并无法确定,我们约定,构建的矩阵为\(\begin{bmatrix} X_{1,1} & X_{1,2} & \cdots & X_{1,M-1} & X_{1,M} \\ \vdots & \vdots & \ddots & \vdots & \vdots \\ X_{N-1,1} & X_{N-1,2} & \cdots & X_{N-1,M-1} & 0 \\ X_{N,1} & X_{N,2} & \cdots & X_{N,M-1} & X_{N,M} \end{bmatrix}\) ,Bob输出的矩阵为 \(\begin{bmatrix} T_{1,1} & \cdots & \cdots & T_{1,M-1} & T_{1,M} \\ \vdots & & \ddots & \vdots & \vdots \\ T_{N-1,1} & \cdots & \cdots & T_{N-1,M-1} & 0 \\ T_{N,1} & \cdots & T_{N,M-2} & Y_{N,M-1} & Y_{N,M} \end{bmatrix}\) ,正解输出的矩阵为 \(\begin{bmatrix} T_{1,1} & \cdots & \cdots & T_{1,M-1} & T_{1,M} \\ \vdots & & \ddots & \vdots & \vdots \\ T_{N-1,1} & \cdots & \cdots & T_{N-1,M-1} & 0 \\ T_{N,1} & \cdots & T_{N,M-2} & Z_{N,M-1} & Z_{N,M} \end{bmatrix}\) 。
第二步:分析。注意到 \(Y_{N-1,M}\) 和 \(Z_{N-1,M}\) 的位置被赋值为了 \(0\) ,这是为了保证答案只能由 \(Y_{N,M-1}\) 和 \(Z_{N,M-1}\) 传递得到,去除了不必要的讨论。
根据Bob的错误,我们假设错误只发生在最后一步 \(\tt max\) : \(Y_{N,M-1}>Z_{N,M-1}\) ,Bob的程序显然会选择 \(Y_{N,M-1}\) ,我们只需要使得选择 \(Z_{N,M-1}\) 时答案更优即可。
再进一步分析 \(Y_{N,M-1}\) 和 \(Z_{N,M-1}\) 的来历,我们发现,它们一个是由 \(T_{N,M-2}\&X_{N,M-1}\) 得到,一个是由 \(T_{N-1,M-1}\&X_{N,M-1}\) 得到,而要使得Bob的程序错误,只需要使得 \(T_{N,M-2}\) 和 \(T_{N-1,M-1}\) 不同,除此之外没有别的条件。
我们考虑收敛矩阵,发现最小只需要 \(2*3\) 的矩阵,即可使得 \(T_{N,M-2}\) 和 \(T_{N-1,M-1}\) 不同。
第三步:构建。至此,我们进一步约定,构建的矩阵为 \(\begin{bmatrix} \mathcal A & \mathcal B & 0 \\ \mathcal D & \mathcal E & \mathcal F \end{bmatrix}\) ,Bob输出的矩阵为 \(\begin{bmatrix} a & b & 0 \\ d & e & f \end{bmatrix}\) ,正解输出的矩阵为 \(\begin{bmatrix} A & B & 0 \\ D & E & F \end{bmatrix}\) 。
由于需要让答案恰好相差 \(K\) ,我们不妨使 \(f=0,F=K\) ,那么可以得到 \(\mathcal F=K\) 。
那么可以得到 \(e=X,E=K\) ,其中,\(X\) 这个数不能与 \(K\) 在某一位上均为 \(1\) 。在这里,出题人简化了 \(X\) 的取值过程:根据所给定的取值,我们只需要取 \(2^{17}=131072\) ,就能保证对于任意的 \(K\) ,\(X\) 均满足条件。
这里出题人如果没有做简化的话,\(X\) 的取值可以为 \(2^{log_2^k+1}\) ,即 \(1\) 后面 \(k+1\) 个 \(0\) ,满足上述条件。
同理,可以得到一组可行解 \(\mathcal {E=A}=X+K,\mathcal B=X,\mathcal D=K\) ,直接输出即可。
AC代码
点击查看代码
void Solve() {
int t = (1 << 17);
int k; cin >> k;
cout << 2 << " " << 3 << endl;
cout << t + k << " " << t << " " << 0 << endl;
cout << k << " " << t + k << " " << k << endl;
}
文 / WIDA
2022.05.15 成文
首发于WIDA个人博客,仅供学习讨论