Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov!
只会两道题,改加训了 qwq
A. Dreamoon and Ranking Collection
注意到 \(a_i\) 的范围只有 \(100\),那么我们暴力枚举 \([1,100]\),不存在某个数字就零 \(k--\) 即可
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov!
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/A
When: 2022-03-16 21:05:05
Memory: 256MB
Time: 1000ms
********************/
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int n, k; cin >> n >> k;
vector<int> cnt(101);
for(int i = 0; i < n; i ++ ) {
int x; cin >> x;
cnt[x] = true;
}
for(int i = 1; i <= 100; i ++ ) {
if(cnt[i]) continue;
if(!k) break;
k --;
cnt[i] ++;
}
if(k) cout << 100 + k << "\n";
else {
int ans = 0;
for(int i = 1; i <= 100; i ++ ) {
if(!cnt[i]) break;
ans = i;
}
cout << ans << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while(T --) solve();
return 0;
}
B. Dreamoon Likes Permutations
题意是将数组分为两部分,每个部分满足他们是 \(1-x\) 的一个排列,并且没有重复的数字
正序和倒序分别标记一下每个位置是否满足前缀或后缀是否符合条件,两者都符合条件的位置就是我们的答案
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov!
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/B
When: 2022-03-16 21:05:06
Memory: 256MB
Time: 2000ms
********************/
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int n; cin >> n;
vector<int> a(n); for(int &x: a) cin >> x;
vector<bool> l(n), r(n);
//正序
vector<int> cnt(n + 2);
int now = 0;
for(int i = 0; i < n; i ++ ) {
if(cnt[a[i]]) break;
cnt[a[i]] ++;
while(cnt[now + 1]) now ++;
// if(!i)
if(now == i + 1) l[i] = true;
}
//逆序
vector<int> ct(n + 2);
reverse(a.begin(), a.end());
now = 0;
for(int i = 0; i < n; i ++ ) {
if(ct[a[i]]) break;
ct[a[i]] ++;
while(ct[now + 1]) now ++;
if(now == i + 1) {
r[n - i - 1] = true;
}
}
vector<array<int, 2> > ans;
for(int i = 0; i < n - 1; i ++ ) {
if(l[i] && r[i + 1]) { //注意是 i 和 i + 1
ans.push_back({i + 1, n - i - 1});
}
}
cout << (int)ans.size() << "\n";
for(auto [k, v] : ans) {
cout << k << " " << v << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while(T --) solve();
return 0;
}
C. Dreamoon Likes Coloring
题目描述
给定长为 \(n\) 的格子和 \(m\) 种颜色
\(Dreamoon\) 会依次刷这 \(m\) 种颜色,对于第 \(i\) 种颜色,他会从第 \(p_i\) 格开始刷到第 \(p_i + l_i - 1\) 格,\(p_i\) 不能超过 $ n - l_i +1$ 或小于 \(1\),这样会超出格子。
您的任务是找出一组 \(p_i\), 使得 \(Dreamoon\) 刷完所有颜色之后每种颜色至少出现了一次,且每个格子都被刷上了颜色。
数据范围
\(1 \le m \le n \le 10^5, 1 \le l_i \le n\)
思路
首先我们先考虑 \(-1\) 的情况
如果所有格子长度的和 \(s\) 比 \(n\) 小的话,无论如何我们也无法涂完所有的格子,那么显然是无解的
题目中说 后染的颜色会覆盖之前染的颜色,那么我们考虑最后染的颜色,他一定是会完全存在与最终的格子上的
那么我们怎么去染某个颜色呢,首先我们要保证剩下的格子是够剩下的染色都存在的。意思就是我们假设剩下\(x\)个颜色没有染,那么空余的格子一定是不少于 \(x\) 的
然后考虑我们不能让某个格子空着,比如 \(222201111\),我们不能让两次染的颜色之间有空隙,那么我们的终点也被限制了
因此我们就既保证了后面的颜色有位置可以放,并且全部的格子都含有颜色,那么这样填下去即可,注意判断是否超过了格子的界限
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov!
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/C
When: 2022-03-16 21:05:07
Memory: 256MB
Time: 2000ms
********************/
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int n, m; cin >> n >> m;
LL s = 0;
vector <int> a(m + 1);
for(int i = 1; i <= m; i ++ ) cin >> a[i], s += a[i];
if(s < n) {
cout << "-1\n";
return;
}
vector<int> ans(m + 1);
int last = n;
for(int i = m; i >= 1; i -- ) {
int idx = max(i, last - a[i] + 1);
if(i > last || idx > n - a[i] + 1) {
cout << "-1\n";
return;
}
ans[i] = idx;
last = ans[i] - 1;
}
if(last > 0) {
cout << "-1\n";
return;
}
for(int i = 1; i <= m; i ++ ) cout << ans[i] << " ";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1; //cin >> T;
while(T --) solve();
return 0;
}
D. Dreamoon Likes Sequences
题意
给定 \(d, m\)
对于一个数组\(a_1, a_2, ... , a_n \le d\) $$, 定义 数组\(b\), \(b_1 = a_1, b_i = a_i \oplus a_{i-1}\)
并且 \(1 \le a_1 \le a_2 \le .... \le a_n\) ,\(1 \le b_1 \le b_2 \le ... \le b_n\)
求数组 \(a\) 有多少种构造方案可以使满足上面的条件,输出方案数 对 \(m\) 取模后的数字
如果存在 \(b\) 数组,那么数组 \(a\) 中, \(a_i\) 的最高位是一定比 \(a_{i-1}\) 的最高位高的
简单来说 就是 __lg(\(a_i\)) > __lg(\(a_{i-1}\)) __lg
函数是求出 某个数字的二进制下最高位是哪一位
令 f(x) = __lg(x)
为什么一定是这样呢?
首先 \(a_i\) 是单调递增的,这说明 \(f(a_i) \le f(a_{i+1})\)
考虑证明:如果\(f(a_i) = f(a_{i+1})\), 那么 \(b_i \le b_{i-1}\)
令\(idx\)是第一个出现 \(f(a_i) = f(a_{i-1})\) 的位置
那么 \(idx\) 之前的位置全都 \(f(a_i) \le f(a_{i-1})\), 因此 \(f(b_i) > f(b_{i-1})\)
令 \(x = f(b_{i-1})\), 显然 \(f(a_{i-1}) = x\),若此时 \(f(a_i) = f(a_{i-1})\),则 \(f(b_i \le x - 1)\)
因此如果 \(f(a_{idx}) == f(a_{idx-1})\),那么必定会出现 \(b_{idx} < b_{idx-1}\)
得证.
因此我们就是在 __lg(x)
的每一个值的区间进行选择,每个区间可以选择一个数字,或者不选
例如当 \(d = 6\) 的时候, __lg(x) = 0
的数字有一个,选择的方案数有两种,__lg(x) = 1
的数字有两个,选择的方案数有三种,__lg(x) = 0
的数字有三个,选择的方案数有四种,所以总方案数就是 \(2 \times 3 \times 4 = 24\),但是其中有一种方案是不合法的,就是一个数字都不选的时候,因此答案应该是 \((24 - 1) \% m\)
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov!
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/D
When: 2022-03-16 21:05:08
Memory: 256MB
Time: 1000ms
********************/
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve() {
int d, m; cin >> d >> m;
LL ans = 1;
for(int i = 0; i < 30; i ++ ) {
if(d < (1 << i)) break;
ans = ans * (min((1 << i + 1) - 1, d) - (1 << i) + 2);
ans %= m;
}
ans --;
if(ans < 0) ans += m;
printf("%lld\n", ans);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while(T --) solve();
return 0;
}
思路
在操作过程中一直都是一个大根堆,那么我们从根节点贪心的往下删即可
点击查看代码
/********************
Author: Nanfeng1997
Contest: Codeforces Round #631 (Div. 2) - Thanks, Denis aramis Shitov!
URL: https://codeforces.com/contest/{getProblemIndexes(problemCurrentPageList[i][0])[0]}/problem/E
When: 2022-03-16 21:05:09
Memory: 256MB
Time: 1500ms
********************/
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1 << 21;
int h, g, a[N], dep[N], mxdep[N], ans[N];
void dfs(int u, int t) { //处理一下深度和高度
dep[u] = t - 1;
if(t == h) {
mxdep[u] = 1; return;
}
dfs(u << 1, t + 1);
dfs(u << 1 | 1, t + 1);
mxdep[u] = mxdep[u << 1] + 1;
}
void pop(int u) {
if(mxdep[u] == 1) { //当他没有儿子的时候就结束咯
mxdep[u] = a[u] = 0; return;
}
if(a[u << 1] > a[u << 1 | 1]) {
a[u] = a[u << 1]; pop(u << 1);
} else {
a[u] = a[u << 1 | 1]; pop(u << 1 | 1);
}
if(a[u << 1] > a[u << 1 | 1]) {
mxdep[u] = mxdep[u << 1] + 1;
} else {
mxdep[u] = mxdep[u << 1 | 1] + 1;
}
}
void solve() {
cin >> h >> g;
LL sum = 0;
for(int i = 1; i < 1 << h; i ++ ) {
cin >> a[i]; sum += a[i];
}
dfs(1, 1);
int cnt = 0;
//mxdepth 表示当前节点往下走并且每次走权值大的儿子的最大 高度
//depth 表示从 1 号根节点向下的深度
for(int i = 1; i < 1 << g; i ++ ) {
while(mxdep[i] > g - dep[i]) {//如果他可以被删,那么一定要把它删了
ans[++ cnt] = i;
sum -= a[i];
pop(i);
}
}
cout << sum << "\n";
for(int i = 1; i <= (1 << h) - (1 << g); i ++ ) {
cout << ans[i] << " ";
}
cout << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while(T --) solve();
return 0;
}