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\) 是坐电梯。
那么如果上一次上楼是通过楼梯,状态转移方程为:

\[f_{i,0}=\min\{f_{i-1,0},f_{i-1,1}\}+a_i \]

因为走楼梯不需要等电梯的时间,所以直接走上来即可。
同理,若上一次上楼是电梯,状态转移方程为:

\[f_{i,1}=\min\{f_{i-1,0}+c,f_{i-1,1}\}+b_i \]

因为乘坐电梯是需要等待时间的,因此走楼梯需要额外加上 \(c\)。但是如果你原本就是乘坐电梯的,那么你就无需再等待。接下来注意初始状态:

\[f_{1,0}=0,f_{1,1}=c \]

走楼梯可以直接上楼,设置为 \(0\) 即可。乘坐电梯的话一开始也要等待,所以设置为 \(c\)。再来看终止状态,因为每一次上楼到第 \(i\) 层是独立的,因此你无需在意本次最小值是走楼梯还是电梯对后面的影响。因此,对于第 \(i\) 层楼的结束状态为:

\[\min\{f_{i,0},f_{i,1}\} \]

代码

#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;
}
posted @ 2023-10-05 16:50  cyf1208  阅读(23)  评论(0编辑  收藏  举报