CF1249(Div. 3) 题解(A to E)
\(\texttt{F}\) 忘(不)记(会)写了。
A Yet Another Dividing into Teams 题解
题目大意
有一个长度为 \(n\) 的序列 \(a_1,a_2,\cdots,a_n\),将他们分为若干组,使得每一组没有两个数的差为 \(1\),使分的组数尽可能少。
解题思路
排完序后对于任意 \(1\le i< n\) 均有 \(a_i\) 与 \(a_{i+1}\) 最接近,所以若存在 \(a_{i+1}-a_i=1\) 则必定要分成两组,否则一组。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int a[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
int ans = 1;
sort(a + 1, a + n + 1);
for(int i = 2; i <= n; i++) {
if(a[i - 1] + 1 == a[i]) {
ans = 2;
break;
}
}
cout << ans << "\n";
}
return 0;
}
B1 & B2 Books Exchange 题解
题目大意
有 \(n\) 个小朋友每人手里有一本书,给定一个排列 \(a\),表示每一轮第 \(i\) 个小朋友会把书给 \(a_i\)。求每个小朋友的书会在几轮后回到自己手里。
解题思路
暴搜寻找即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 205;
int t, n, a[N];
inline int dfs(int cur, int x, int y) {
if(y == x && cur != 0) {
return cur;
}
return dfs(cur + 1, a[x], y);
}
int main() {
ios::sync_woth_stdio(0);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--) {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i <= n; i++) {
cout << dfs(0, i, i) << " ";
}
cout << "\n";
}
return 0;
}
C1 & C2 Good Numbers 题解
题目大意
给定 \(n\),求不小于 \(n\) 的最小数 \(x\),使得 \(x\) 可以分解成不同的 \(3\) 的幂之和。其中 \(C2\) 的数据规模更大。
解题思路
我们只需要算出 \(3^i\) 的前缀和 \(s_i\),然后从大到小判断是否要选 \(3^i\) 作为 \(x\) 的一部分,如果 已选数字之和加上 \(s_{i-1}\le x\) 那么说明当前 \(3^i\) 不用选。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 1e18;
int n, ans, sum[105], a[105];
inline int solve(long long x) {
int ans = 0;
for(int i = 38; i >= 1; i--) {
if(ans + sum[i - 1] >= x) {
continue;
} else {
ans += a[i];
}
}
if(ans < x) {
ans += 1;
}
return ans;
}
signed main() {
sum[0] = 1;
a[0] = 1;
long long cnt = 3;
for(int i = 1; i <= 100 && sum[i - 1] <= INF; i++) {
sum[i] = sum[i - 1] + cnt;
a[i] = cnt;
cnt *= 3;
}
int t;
cin >> t;
while(t--) {
cin >> n;
cout << solve(n) << "\n";
}
return 0;
}
D1 & D2 Too Many Segments 题解
题目大意
给定 \(n\) 个区间,要求删除最少数量的区间,使得每个点被覆盖的次数不超过 \(k\),求方案。
解题思路
我们首先要做的事情就是能够计算出当前点 \(x\) 处被几条线段覆盖的问题。我们需要先将所有线段按左端点排序,用差分维护线段结束,然后枚举每条线段,每枚举一个线段我们就让 \(cnt+1\), 表示当前点 a[i].l
有 \(cnt\) 个线段覆盖,同时 sum[a[i].r]--
,表示线段在 a[i].r
在这里结束。当我们在判断当前点 a[i].l
被几条线段覆盖时,还需要去掉右端点已经小于 a[i].l
的线段,这样就得到了当前点 a[i].l
有 \(cnt\) 个线段覆盖。然后判断 \(cnt>k\),如果 \(cnt>k\) 说明需要去掉线段,我们优先去掉右端点最大的线段。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, k;
int sum[N], cnt, pos;
vector<int> ans;
priority_queue<pair<int, int> > p;
struct node {
int l, r, id;
} a[N];
inline bool cmp(node x, node y) {
if(x.l == y.l) {
return x.r > y.r;
}
return x.l < y.l;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> a[i].l >> a[i].r;
a[i].id = i;
}
sort(a + 1, a + 1 + n, cmp);
for(int i = 1; i <= n; i++) {
p.push(make_pair(a[i].r, a[i].id));
cnt++;
sum[a[i].r]--;
while(pos < a[i].l) {
cnt = cnt + sum[pos];
pos++;
}
if(a[i].l != a[i + 1].l) {
while(cnt > k) {
while(!p.empty() && p.top().first < a[i].l) {
p.pop();
}
ans.push_back(p.top().second);
sum[p.top().first]++;
p.pop();
cnt--;
}
}
}
cout << ans.size() << "\n";
for(int i = 0; i < ans.size(); i++) {
cout << ans[i] << " ";
}
cout << "\n";
return 0;
}
E By Elevator or Stairs? 题解
题目大意
给出 \(n,c\),表示有 \(n\) 层楼,等电梯需要 \(c\) 时间。
给出两行数,每行有 \(n-1\) 个数,
- 第一行 \(a_i\) 代表从 \(1\) 楼到每层楼走楼梯需要的时间。
- 第二行 \(b_i\) 代表从 \(1\) 楼到每层楼乘电梯需要的时间。
从电梯转电梯不需要等待时间,从楼梯转楼梯也不需要等待时间,
但是从楼梯转电梯需要算上等待的时间 \(t\)。
解题思路
令 \(f_{i,j}\) 表示走到第 \(i\) 层楼的最少时间,同时从 \(i-1\) 到达 \(i\) 是通过方法 \(j\) 来的,其中 \(j=0\) 表示是走楼梯,\(j=1\) 是坐电梯。
那么如果上一次上楼是通过楼梯,状态转移方程为:
因为走楼梯不需要等电梯的时间,所以直接走上来即可。
同理,若上一次上楼是电梯,状态转移方程为:
因为乘坐电梯是需要等待时间的,因此走楼梯需要额外加上 \(c\)。但是如果你原本就是乘坐电梯的,那么你就无需再等待。接下来注意初始状态:
走楼梯可以直接上楼,设置为 \(0\) 即可。乘坐电梯的话一开始也要等待,所以设置为 \(c\)。再来看终止状态,因为每一次上楼到第 \(i\) 层是独立的,因此你无需在意本次最小值是走楼梯还是电梯对后面的影响。因此,对于第 \(i\) 层楼的结束状态为:
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, c, a[N], b[N], dp[N][2];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> c;
for(int i = 2; i <= n; i++) {
cin >> a[i];
}
for(int i = 2; i <= n; i++) {
cin >> b[i];
}
dp[2][0] = a[2];
dp[2][1] = b[2] + c;
for(int i = 3; i <= n; i++) {
dp[i][0] = min(dp[i - 1][0] + a[i], dp[i - 1][1] + a[i]);
dp[i][1] = min(dp[i - 1][0] + b[i] + c, dp[i - 1][1] + b[i]);
}
for(int i = 1; i <= n; i++) {
cout << min(dp[i][0], dp[i][1]) << " ";
}
return 0;
}