牛客练习赛61

比赛地址:

https://ac.nowcoder.com/acm/contest/5026

前言:这场比赛感觉自己脑袋不太清醒,导致了罚时爆表A题把continue写成了retrurn居然一直都没发现,C也不明原因WA了好多次,好在最后一下连过三题苟了个四题尾总算没有掉分。(我真是个憨憨)


A. 打怪

基本思路:

  1. 由于勇士是先手,所以勇士的攻击如果大于怪物的血量,一定能无限秒杀怪物输出-1;
  2. 否则我们看下在打一只怪的时候勇士会被攻击到几次,就能算出打一只怪勇士血量的损失(注意先后手);
  3. 然后用勇士的血量去除打一只怪的血量损失理论上就是答案了,但是注意如果刚好整除的话,由于勇士每次是给与怪物最后一击的,所以如果整除说明最后一次怪物先把勇士打死了,所以这个时候减个一;

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define ll long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define pdd pair <double, double>
#define mp(a, b) make_pair(a, b)
#define INF 1e18
inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}
int a,b,c,d;
signed main() {
  IO;
  int t;
  cin >> t;
  while (t-- > 0) {
    cin >> a >> b >> c >> d;
    if (b >= c) { // 能秒杀怪,无损失;
      cout << -1 << '\n';
      continue;
    }
    int k1 = c % b == 0 ? c / b - 1 : c / b;//整除的话,由于先手要减一;
    int z = d * k1;//杀一只怪勇士血量的损失;
    if (a % z == 0) cout << a / z - 1;//整除要减一;
    else cout << a / z;
    cout << '\n';
  }
  return 0;
}

B. 吃水果

基本思路:

这题应该很简单贪心的策略,我们要想办法将苹果和香蕉的数量凑到相同再一起吃完,所以我们将苹果和香蕉里较小的那个先最快的加到一样多,然后再一天天吃就是了。

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define pdd pair <double, double>
#define mp(a, b) make_pair(a, b)
#define INF 1e18
inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}
int n,m;
signed main() {
  IO;
  int t;
  cin >> t;
  while (t-- > 0) {
    cin >> n >> m;
    int cnt = 0;
    if(n > m) swap(n,m); // 令n是小的那个;
    int ans = m;
    while (n < m) { //加到两者数目一样多;
      n += n;
      cnt++;
    }
    ans += cnt;
    cout << ans << endl;
  }
  return 0;
}

C. 四个选项

基本思路:

因为只有12道题,而且限制条件很多,明显的dfs+剪枝就能过,基本上就是一个裸的dfs再根据每种选项的数量和那m个额外条件剪枝就是了,就是出了一个莫名其妙的问题让我WA了好久,具体实现可以参考代码。

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define ll long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define pdd pair <double, double>
#define mp(a, b) make_pair(a, b)
#define INF 1e18
inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}
int na,nb,nc,nd,m;
int ans = 0;
map<int,vector<int>> memo;
void dfs(int s,string str,int a,int b,int c,int d) {
  if (a > na || b > nb || c > nc || d > nd) return; // 数量超过了就return;
  if (s == 12) {
    if (a == na && b == nb && c == nc && d == nd) ans++;
    return;
  }
  for (int i = 0; i < 4; i++) {
    char ct = (char) ('A' + i);
    bool v = true;
    if (memo.count(s)) { // 要满足和这些都相同;
      for (auto it : memo[s]) {
        if (it < s && ct != str[it]) {
          v = false;
          break;
        }
      }
    }
    if (!v) continue;
    dfs(s + 1, str + ct, i == 0 ? a + 1 : a, i == 1 ? b + 1 : b, i == 2 ? c + 1 : c, i == 3 ? d + 1 : d);
  }
}
signed main() {
  IO;
  cin >> na >> nb >> nc >> nd >> m;
  memo.clear();
  ans = 0;
  rep(i, 1, m) {
    int x, y;
    cin >> x >> y;
    x--, y--;
    if (y > x) swap(x, y);
    memo[x].push_back(y);
    memo[y].push_back(x);//不加这个就WA了,虽然我感觉只往前找没问题...;
  }
  dfs(0, "", 0, 0, 0, 0);
  cout << ans << '\n';
  return 0;
}

D. 最短路变短了

基本思路:

  1. 这题其实比较套路这种将边反向啊,或者两个点再多连一条边和原先最短路比的,都差不多是这么做;
  2. 我们先跑一遍dijkstra算出起点到所有节点的最短路,记录在数组dis1中,然后再反向建边算出终点到所有节点的最短路记录在数组dis2中;
  3. 我们将边反向后经过这条反向边(原)(u,v)的最短路就应该为 = dis1[v] + dis2[u] + w,将这条经过反向边的最短路和原先的最短路比,就能知道新最短路是否变短了;

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define pdd pair <double, double>
#define mp(a, b) make_pair(a, b)
#define INF 1e18
inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}
const int maxn = 1e5 + 10;
const int maxm = 2e5 + 10;
struct Edge{
    int to,next,val;
}edge1[maxm],edge2[maxm];
int n,m;
int head1[maxn],head2[maxn];
int cnt1 = 0, cnt2 = 0;
void add_edge1(int u,int v,int w) {
  edge1[++cnt1] = {v, head1[u], w};
  head1[u] = cnt1;
}
void add_edge2(int u,int v,int w) {
  edge2[++cnt2] = {u, head2[v], w};//反向加的边;
  head2[v] = cnt2;
}
int dis1[maxn],dis2[maxn];
bool vis[maxn];
void dijkstra1(int k) {//正向dijkstra;
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
  while (!q.empty()) q.pop();
  for (int i = 1; i <= n; i++) dis1[i] = INF;
  mset(vis,false);
  dis1[k] = 0;
  q.push(make_pair(0, k));
  while (!q.empty()) {
    int s = q.top().second;
    q.pop();
    if (vis[s]) continue;
    vis[s] = true;
    for (int i = head1[s]; i != -1; i = edge1[i].next) {
      if (dis1[edge1[i].to] > dis1[s] + edge1[i].val) {
        dis1[edge1[i].to] = dis1[s] + edge1[i].val;
        q.push(make_pair(dis1[edge1[i].to], edge1[i].to));
      }
    }
  }
}
void dijkstra2(int k) {//反向dijkstra;
  priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
  while (!q.empty()) q.pop();
  for (int i = 1; i <= n; i++) dis2[i] = INF;
  mset(vis, false);
  dis2[k] = 0;
  q.push(make_pair(0, k));
  while (!q.empty()) {
    int s = q.top().second;
    q.pop();
    if (vis[s]) continue;
    vis[s] = true;
    for (int i = head2[s]; i != -1; i = edge2[i].next) {
      if (dis2[edge2[i].to] > dis2[s] + edge2[i].val) {
        dis2[edge2[i].to] = dis2[s] + edge2[i].val;
        q.push(make_pair(dis2[edge2[i].to], edge2[i].to));
      }
    }
  }
}
vector<int> memo[maxm];//记录一下边;
signed main() {
  IO;
  n = read(),m = read();
  cnt1 = 0, cnt2 = 0;
  mset(head1, -1);
  mset(head2, -1);
  rep(i, 1, m) {
    int u, v, w;
    u = read(),v = read(),w = read();
    memo[i] = {u, v, w};
    add_edge1(u, v, w);
    add_edge2(u, v, w);
  }
  dijkstra1(1);
  dijkstra2(n);
  int temp = dis1[n];
  int q;
  q = read();
  while (q-- > 0) {
    int x;
    x = read();
    int u = memo[x][0], v = memo[x][1], w = memo[x][2];
    if (dis1[v] + dis2[u] + w < temp) { // 记得v,和u要相反;
      cout << "YES" << '\n';
    } else cout << "NO" << '\n';
  }
  return 0;
}