P9381 [THUPC 2023 决赛] 那些脑海里最珍贵的

发布时间 2023-07-02 19:45:25作者: bykem

小清新大模拟(?

写起来挺顺的,就是浮点误差那块整破防了,最后问了神虎用了科学计数法存浮点数才过

stO 神虎 Orz

坑点:

  • 注意精度误差
  • 死亡后要清除 Average 的主动技能,防止重复触发死亡处理导致被动技能被弄乱
  • Average 的主动技能里的 “\(3\) 个回合” 指的是南北两边各行动一次算一个回合,且回合结束时只清算敌队的 Average 的主动技能效果
  • 如果 Weak 的技能没有造成实际上的体力回复(而不仅仅是当体力达到上限时),不输出对应信息
  • 输出信息格式,注意空格

小技巧:使用 C++ 风格输入输出时,可以使用

struct Skiper {
} skip;
istream &operator>>(istream &in, Skiper skip) {
  for (; in.get() != '=';) {
  }
  return in;
}

来跳过形如 [sth.]= 的输入,用法是直接 cin >> skip

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;
using i128 = __int128_t;

struct Skiper {
} skip;
istream &operator>>(istream &in, Skiper skip) {
  for (; in.get() != '=';) {
  }
  return in;
}

i128 pow10[39];
auto InitPow10 = []() {
  for (int i = pow10[0] = 1; i <= 38; ++i) {
    pow10[i] = pow10[i - 1] * 10;
  }
  return 0;
}();

struct Float {
  i128 mag;
  int exp;

  Float(i128 mag = 0, int exp = 0) : mag(mag), exp(exp) { Shrink(); }
  void Shrink() {
    for (; mag && (mag % 10 + 10) % 10 == 0; mag /= 10, ++exp) {
    }
  }
  Float operator+(const Float &o) const {
    int rexp = min(exp, o.exp);
    return Float(mag * pow10[exp - rexp] + o.mag * pow10[o.exp - rexp], rexp);
  }
  Float operator-() const { return Float(-mag, exp); }
  Float operator-(const Float &o) const { return *this + (-o); }
  Float operator*(const Float &o) const { return Float(mag * o.mag, exp + o.exp); }
  int operator*(int o) const {
    __int128_t s = mag * o;
    if (exp < 0) {
      return s / pow10[-exp];
    }
    return s * pow10[exp];
  }
  int operator/(const Float &o) const {
    int rexp = min(exp, o.exp);
    return (mag * pow10[exp - rexp]) / (o.mag * pow10[o.exp - rexp]);
  }
  Float operator+=(const Float &o) { return *this = *this + o; }
  Float operator-=(const Float &o) { return *this = *this - o; }
  bool operator<(const Float &o) const {
    int rexp = min(exp, o.exp);
    return (mag * pow10[exp - rexp]) < (o.mag * pow10[o.exp - rexp]);
  }
};
Float operator"" lf(const char *s) {
  int p = 0, c = 0, d = 0;
  for (int i = 0; s[i] != '\0'; ++i) {
    if (s[i] == '.') {
      d = 1;
    } else {
      p = p * 10 + s[i] - '0';
      c -= d;
    }
  }
  return Float(p, c);
}
istream &operator>>(istream &in, Float &x) {
  for (char c; (c = in.get()) != 'e'; x.mag = x.mag * 10 + c - '0') {
  }
  in >> x.exp;
  x.Shrink();
  return in;
}

const string kTeam[2] = {"South", "North"};
const string kType[3] = {"Weak", "Average", "Strong"};
const char kWeaponType[3] = {'B', 'G', 'M'};
const Float kTypeBuff[3][3] = {{1lf, 0.9lf, 1.1lf}, {1.1lf, 1lf, 0.9lf}, {0.9lf, 1.1lf, 1lf}};
const Float kPosBuff[6] = {1.25lf, 1lf, 0.75lf, 0lf, 0.75lf, 1lf};
const int kPos[7] = {0, 5, 3, 1, 2, 4, 6};
const int kWhere[7] = {0, 3, 4, 2, 5, 1, 6};
const Float kPassSkill[3][6] = {{0lf, 0.013lf, 0.016lf, 0.019lf, 0.022lf, 0.025lf}, {0lf, 0.01lf, 0.02lf, 0.03lf, 0.04lf, 0.05lf}, {0lf, 0.01lf, 0.02lf, 0.03lf, 0.04lf, 0.05lf}};
const Float kSkill[3][6] = {{0lf, 0.1lf, 0.12lf, 0.15lf, 0.17lf, 0.2lf}, {0lf, 0.06lf, 0.07lf, 0.08lf, 0.09lf, 0.1lf}, {1lf, 2.1lf, 2.17lf, 2.24lf, 2.32lf, 2.4lf}};

int n[2], last[2], turn;
Float buff[3][2] = {{}, {1lf, 1lf}, {1lf, 1lf}};
struct Player {
  int team, id, type, level, max_hp, hp, skill_level, passive_skill_level, weapon_type;
  Float atk, def, weapon_atk;
  bool dead;
  Float skill_buff = 1lf;
  Float poison;
  int poison_turn;

  void PassiveSkill(bool g) {
    Float _ = kPassSkill[type][passive_skill_level];
    buff[type][team] += g ? _ : -_;
  }
  void TakeDamage(const Player &from, Float damage, int atkpos, int ddgpos) {
    Float real_atk = damage * from.skill_buff * min(1.1lf, buff[2][from.team]) * kTypeBuff[from.type][type] * kPosBuff[(atkpos - ddgpos + 6) % 6];
    Float real_def = def * min(1.1lf, buff[1][team]);
    int real_damage = real_atk / real_def;
    hp = max(0, hp - real_damage);
    cout << kTeam[team] << ' ' << id << " took " << real_damage << " damage from " << kTeam[from.team] << ' ' << from.id << " -> " << hp << '/' << max_hp << '\n';
    CheckDead();
  }
  void CheckDead() {
    if (!hp) {
      dead = 1, poison = 0lf, poison_turn = 0;
      PassiveSkill(0);
    }
  }
  void Recovered(Float d) {
    int delta = d * max_hp;
    if (hp != min(max_hp, hp + delta)) {
      hp = min(max_hp, hp + delta);
      cout << kTeam[team] << ' ' << id << " recovered +" << delta << " hp -> " << hp << '/' << max_hp << '\n';
    }
  }
} p[2][7];

void CheckWin() {
  for (int o = 0; o < 2; ++o) {
    bool f = 1;
    for (int i = 1; i <= n[o]; ++i) {
      f &= p[o][i].dead;
    }
    if (f) {
      cout << "Team " << kTeam[!o] << " won.";
      return;
    }
  }
}
int GetActor(int o) {
  int i = last[o] % n[o] + 1;
  for (; p[o][i].dead; i = i % n[o] + 1) {
  }
  return last[o] = i;
}
vector<int> GetNeighbor(int o, int x) {
  vector<int> neighbor(1, x);
  for (int d = -1; d <= 1; ++d) {
    if (!d) {
      continue;
    }
    int i = kWhere[x] + d;
    for (; 1 <= i && i <= 6 && kPos[i] <= n[o] && p[o][kPos[i]].dead; i += d) {
    }
    if (1 <= i && i <= 6 && kPos[i] <= n[o]) {
      neighbor.push_back(kPos[i]);
    }
  }
  return neighbor;
}

int main() {
  ios_base::sync_with_stdio(0), cin.tie(0);
  cin >> n[0] >> n[1];
  for (int o = 0; o < 2; ++o) {
    for (int i = 1; i <= n[o]; ++i) {
      Player &x = p[o][i];
      x.team = o, x.id = i;
      string s;
      cin >> s;
      x.type = find(kType, kType + 3, s) - kType;
      cin >> skip >> x.level >> skip >> x.max_hp >> skip >> x.atk >> skip >> x.def >> skip >> x.skill_level >> skip >> x.passive_skill_level;
      char c;
      cin >> c;
      x.weapon_type = find(kWeaponType, kWeaponType + 3, c) - kWeaponType;
      cin >> skip >> x.weapon_atk;
      x.hp = x.max_hp;
      x.PassiveSkill(1);
    }
  }
  cin >> turn;
  for (int o = 0; turn--; o ^= 1) {
    for (int i = 1; i <= n[o]; ++i) {
      if (!p[o][i].dead) {
        p[o][i].Recovered(min(0.05lf, buff[0][o]));
      }
    }
    string type;
    int target, atkpos, ddgpos;
    cin >> type >> skip >> target;
    Player &actor = p[o][GetActor(o)];
    Player &t = p[!o][target];
    if (type == "Basicattack") {
      cin >> skip >> atkpos >> skip >> ddgpos;
      t.TakeDamage(actor, actor.atk * actor.weapon_atk, atkpos, ddgpos);
      actor.skill_buff = 1;
    } else if (type == "Specialattack") {
      cin >> skip >> atkpos >> skip >> ddgpos;
      if (actor.weapon_type == 0) {
        t.TakeDamage(actor, actor.atk * actor.weapon_atk * 1.25lf, atkpos, ddgpos);
      } else if (actor.weapon_type == 1) {
        vector<int> neighbor = GetNeighbor(!o, target);
        for (int _target : neighbor) {
          p[!o][_target].TakeDamage(actor, actor.atk * actor.weapon_atk * (neighbor.size() == 1 ? 1.35lf : (neighbor.size() == 2 ? 0.675lf : 0.45lf)), atkpos, ddgpos);
        }
      } else {
        t.TakeDamage(actor, actor.atk * actor.weapon_atk * 1.15lf, atkpos, ddgpos);
        vector<int> neighbor = GetNeighbor(!o, target);
        for (int _target : neighbor) {
          if (_target != target) {
            p[!o][_target].TakeDamage(actor, actor.atk * actor.weapon_atk * 0.23lf, atkpos, ddgpos);
          }
        }
      }
      actor.skill_buff = 1;
    } else {
      cout << kTeam[o] << ' ' << actor.id << " applied " << kType[actor.type] << " skill to ";
      Float skill_value = kSkill[actor.type][actor.skill_level];
      if (actor.type == 0) {
        cout << kTeam[o] << ' ' << target << '\n';
        p[o][target].Recovered(skill_value);
      } else if (actor.type == 1) {
        cout << kTeam[!o] << ' ' << target << '\n';
        t.poison = skill_value;
        t.poison_turn = 3;
      } else {
        cout << kTeam[o] << ' ' << target << '\n';
        p[o][target].skill_buff = skill_value;
      }
    }
    for (int i = 1; i <= n[!o]; ++i) {
      Player &_p = p[!o][i];
      if (!_p.dead && _p.poison_turn) {
        int real_damage = _p.poison * _p.max_hp;
        _p.hp = max(0, _p.hp - real_damage);
        cout << kTeam[!o] << ' ' << i << " took " << real_damage << " damage from skill -> " << _p.hp << '/' << _p.max_hp << '\n';
        if (!_p.hp) {
          _p.CheckDead();
        }
        --_p.poison_turn;
      }
    }
    for (int _o = 1; _o >= 0; --_o) {
      cout << kTeam[_o] << ":";
      for (int i = 1; i <= 6; ++i) {
        if (kPos[i] <= n[_o]) {
          cout << ' ' << p[_o][kPos[i]].hp << '/' << p[_o][kPos[i]].max_hp;
        }
      }
      cout << '\n';
    }
    cout << '\n';
  }
  CheckWin();
  return 0;
}