Educational Codeforces Round 84 部分题解
A. Sum of Odd Integers
题意:
给你两个整数 \(n\) 和 \(k\)。你的任务是找出 \(n\) 是否可以表示为 \(k\)个不同的正奇数(不能被 2 整除)整数的和。
思路:
先把满足条件的最小的 \(n\) 算出来 \(k^2\),然后判断 \(n\) 是否足够大以及奇偶性是否正确。
代码
点击查看代码
void solve() {
int n, k;
cin >> n >> k;
int s=k*k;
if(n>=s&&(n-s)%2==0){
cout<<"YES\n";
}else{
cout<<"NO\n";
}
}
B. Princesses and Princes
题意:
有 \(n\) 个女孩和 \(n\) 个男孩(编号均为 \(1\) 到 \(n\) )。每个女孩有一个心仪男孩的列表。按女孩编号顺序进行匹配:对于每个女孩,从她列表中尚未被选中的男孩中选择编号最小的进行匹配;若列表中没有可选男孩,则她保持单身。
在匹配开始前,允许给恰好一个女孩添加一个原本不在她列表中的男孩,目标是使最终成功匹配的女孩数量增加。如果存在这样的添加方案,输出任意一种(即女孩编号和添加的男孩编号);否则,说明匹配已经最优。
思路:
读完题意直接模拟即可。
代码
点击查看代码
void solve() {
int n;
cin >> n;
vector<bool> vis(n + 1, false);
int s = -1;
for (int i = 1; i <= n; i++) {
int k;
cin >> k;
bool f = false;
for (int j = 0; j < k; j++) {
int x;
cin >> x;
if (!f && !vis[x]) {
vis[x] = true;
f = true;
}
}
if (!f && s == -1) {
s = i;
}
}
if (s == -1) {
cout << "OPTIMAL" << endl;
return;
}
int s1 = -1;
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
s1 = i;
break;
}
}
if (s1 == -1) {
cout << "OPTIMAL" << endl;
} else {
cout << "IMPROVE" << endl;
cout << s << " " << s1 << endl;
}
}
C. Game with Chips
题意:
给定一个n×m网格,初始有k个芯片,每个芯片有初始位置。每次操作可以将所有芯片同时向四个方向之一移动(遇到边界则不动)。每个芯片有一个目标位置。要求通过一系列操作(不超过2nm次),使得每个芯片都在某个时刻经过其目标位置。求操作序列或判断不可能。
思路:
题目只要求经过目标点,所以先用n+m-2次将芯片都堆到边角上,在用nm-1次遍历一遍矩阵。
代码
点击查看代码
void solve() {
int n,m,k;
cin>>n>>m>>k;
int x,y;
for(int i=0;i<k;i++) cin>>x>>y;
for(int i=0;i<k;i++) cin>>x>>y;
string ans;
for(int i=0;i<n-1;i++) ans+='U';
for(int i=0;i<m-1;i++) ans+='L';
for(int i=0;i<n;i++){
if(i%2==0){
for(int j=0;j<m-1;j++) ans+='R';
}else{
for(int j=0;j<m-1;j++) ans+='L';
}
if(i!=n-1) ans+='D';
}
cout<<ans.size()<<endl;
cout<<ans<<endl;
}
D. Infinite Path
题意:
给定一个排列 p 和颜色数组 c,定义无限路径:i, p[i], p[p[i]], p[p[p[i]]] ... 且所有元素颜色相同。定义排列乘法:c = a × b 意味着 c[i] = b[a[i]]。p^k 是 k 个 p 的乘积。需要找到最小的正整数 k,使得在排列 p^k 中存在一个无限路径。
思路:
这道题文字有些不好描述,建议自己整几个排序操作一下。
比较显然的是,将一个排列的所有无限路径取出来,实际上是若干个环。
p × p 意味着 p[p[i]],而 p^k 意味着 p[p[……]] 嵌套 k 次。这实际上就是在无限路径上跳了 k 次,若 i 在 p 上所在的环的大小为 T,则 i 在 p^k 上所在的环的大小为 \(\frac{T}{\gcd(k,T)}\),因此我们只要检查所有 T 的因数对应的环是否同色即可。
代码
点击查看代码
void solve() {
int n;
cin >> n;
vector<int> p(n), c(n);
for (int i = 0; i < n; ++i) {
cin >> p[i];
p[i]--;
}
for (int i = 0; i < n; ++i) {
cin >> c[i];
}
vector<int> vis(n, 0);
int ans = n;
for (int i = 0; i < n; ++i) {
if (vis[i]) continue;
vector<int> q;
int x = i;
while (!vis[x]) {
vis[x] = 1;
q.push_back(x);
x = p[x];
}
int len = q.size();
vector<int> col(len);
for (int j = 0; j < len; ++j) {
col[j] = c[q[j]];
}
vector<int> divs;
for (int d = 1; d * d <= len; ++d) {
if (len % d == 0) {
divs.push_back(d);
if (d != len / d) {
divs.push_back(len / d);
}
}
}
sort(divs.begin(), divs.end());
for (int d : divs) {
vector<int> fc(d, -1);
vector<int> res(d, 1);
for (int j = 0; j < len; ++j) {
int r = j % d;
if (fc[r] == -1) {
fc[r] = col[j];
} else if (col[j] != fc[r]) {
res[r] = 0;
}
}
int fl = 0;
for (int r = 0; r < d; ++r) {
if (res[r]) {
fl = 1;
break;
}
}
if (fl) {
ans = min(ans, d);
break;
}
}
}
cout << ans << endl;
}
E. Count The Blocks
题意:
给定 n,考虑所有 n 位数字串(从 000...0 到 999...9)。将每个数字串按“相同数字构成的极大连续段”进行分块。设 f(i) 为所有数字串中,块长恰好为i 的块的总数(1≤i≤n)。求f(1),f(2),…,f(n) 模 998244353 的值。
思路:
方法一:
打表不难发现 n-1 的答案恰好是 n 的答案的后缀,那么我们只要计算 \(f_n(1)\)。
然后再根据数字总数不变,\(\displaystyle n\cdot10^n=\sum_{i=1}^{n}f_n(i)\cdot i\),递推可得答案。
\(\displaystyle n\cdot10^n=\sum_{i=1}^{n}f_n(i)\cdot i\)
\(\displaystyle f_{n-1}(i)=f_n(i+1)\)
\(\displaystyle (n-1)\cdot10^{n-1}=\sum_{i=1}^{n-1}f_{n-1}(i)\cdot i=\sum_{i=1}^{n-1}f_n(i+1)\cdot i\)
\(=\sum_{i=2}^{n}f_n(i)\cdot (i-1)=\sum_{i=2}^{n}f_n(i)\cdot i-\sum_{i=2}^{n}f_n(i)\)
\(=n\cdot10^n-\sum_{i=1}^{n-1}f_{n-1}(i)-f_n(1)\)
(代码写的是另一种递推,实际上是等价的)
代码
点击查看代码
void solve() {
int n;
cin>>n;
vector<int> q;
q.push_back(10);
int s0=10;
int s1=10,s2=10;
for (int i = 2; i <= n; i++) {
s1*=10;
s1%=mod;
s2+=s0;
s2%=mod;
q.push_back((s1*i%mod-s2+mod)%mod);
s0+=(s1*i%mod-s2+mod)%mod;
s2+=(s1*i%mod-s2+mod)%mod;
s2%=mod;
s0%=mod;
// cout<<s0<<' '<<s1<<' '<<s2<<endl;
}
reverse(q.begin(),q.end());
for(auto x:q){
cout<<x<<' ';
}
cout<<endl;
}
方法二:
计数,枚举长度。
首先计算处于左右边界的块,块的位置选择有 \(2\) 种,块的数字的选择有 \(10\) 种,邻居的数字的选择有 \(9\) 种,剩余数字任意,个数为\(2 \cdot 10 \cdot 9 \cdot 10^{n-len-1}\)。
然后是处于中间的块,块的位置选择有 \(n - len - 1\) 种,块的数字的选择有 \(10\) 种,邻居的数字的选择有 \(9\cdot9\) 种,剩余数字任意,个数为\((n - len - 1) \cdot 10 \cdot 9^2 \cdot 10^{n-len-2}\)。
代码
点击查看代码
void solve() {
int n;
cin>>n;
vector<int> t0(n+1);
t0[0] = 1;
for (int i = 1; i <= n; i++) {
t0[i] = t0[i-1] * 10 % mod;
}
for (int i = 1; i <= n; i++) {
if (i == n) {
cout << 10 << "\n";
} else {
int ans = 180 * t0[n-i-1] % mod;
if (n - i - 2 >= 0) {
ans=(ans+ 810 * (n - i - 1) % mod * t0[n-i-2] % mod)%mod;
}
cout << ans << " ";
}
}
}

浙公网安备 33010602011771号