小清新大模拟(?
写起来挺顺的,就是浮点误差那块整破防了,最后问了神虎用了科学计数法存浮点数才过
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;
}