#define _CRT_SECURE_NO_WARNINGS #define SUBMIT 1 #define CODETEST 0 #define PERFORMANCE 0 #define OPTUNE 0 #define EVAL 0 #define TIME_LIMIT 4950 #define TIME_LIMIT_US (TIME_LIMIT * 1000) #define NOT_SUBMIT 0 #define VALIDATION 0 #define IO_FILE 0 #define OUTPUT_INFO 0 #define OUTPUT_FINAL_INFO 0 #define OUTPUT_LOG 0 #define OUTPUT_VISUAL 0 #define FIX_RESULT 0 #ifndef _MSC_VER #pragma GCC target ("avx2") #pragma GCC optimize "O3,omit-frame-pointer,inline" #pragma GCC optimize ("unroll-loops") #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wunused-variable" #endif #define _USE_MATH_DEFINES #include using namespace std; #define FOR(i, s, e) for (int i = int(s); i < int(e); ++i) #define RFOR(i, s, e) for (int i = int(e) - 1; i >= int(s); --i) #define REP(i, n) for (int i = 0, i##_size = int(n); i < i##_size; ++i) #define RREP(i, n) for (int i = int(n) - 1; i >= 0; --i) template inline void chmin(T& a, T b) { if (b < a) { a = b; } } template inline void chmax(T& a, T b) { if (a < b) { a = b; } } template inline constexpr T square(T v) { return v * v; } using TimePoint = chrono::steady_clock::time_point; struct Timer { private: TimePoint startTime_; TimePoint endTime_; public: inline void Init() { startTime_ = chrono::steady_clock::now(); } inline void StartMs(int limit) { endTime_ = startTime_ + chrono::milliseconds(limit); } inline void StartUs(int limit) { endTime_ = startTime_ + chrono::microseconds(limit); } inline void Join() { } inline bool IsOver() const { return chrono::steady_clock::now() >= endTime_; } inline int ElapseTimeMs() const { return (int)chrono::duration_cast(chrono::steady_clock::now() - startTime_).count(); } inline int ElapseTimeUs() const { return (int)chrono::duration_cast(chrono::steady_clock::now() - startTime_).count(); } inline int LeftToUS(const TimePoint& limit) const { return (int)chrono::duration_cast(limit - chrono::steady_clock::now()).count(); } inline double NowRate() const { return (chrono::steady_clock::now() - startTime_).count() / (double)(endTime_ - startTime_).count(); } inline TimePoint Now() const { return chrono::steady_clock::now(); } inline TimePoint EndTime() const { return endTime_; } TimePoint GetLimitTimePointUs(int limit) const { return startTime_ + chrono::microseconds(limit); } }; Timer timer; template void InstanceRun(int argc, const char* argv[]) { timer.Init(); T* m = new T; m->Run(argc, argv); quick_exit(0); } struct Main; signed main(int argc, const char* argv[]) { cin.tie(0); ios::sync_with_stdio(0); InstanceRun
(argc, argv); } struct ValidateException {}; struct MemoryException {}; #define VALIDATE_ABORT() #define VALIDATE_ASSERT(exp) #define VABORT() VALIDATE_ABORT() #define VASSERT(exp) VALIDATE_ASSERT(exp) #include using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using s8 = int8_t; using s16 = int16_t; using s32 = int32_t; using s64 = int64_t; template struct pr { union { A a; A key; A first; A x; }; union { B b; B value; B second; B y; }; bool operator == (pr const& r) const { return a == r.a && b == r.b; } bool operator != (pr const& r) const { return !((*this) == r); } bool operator < (pr const& r) const { if (a == r.a) { return b < r.b; } return a < r.a; } pr& operator += (pr const& v) { a += v.a; b += v.b; return *this; } pr& operator -= (pr const& v) { a -= v.a; b -= v.b; return *this; } pr operator + (pr const& v) const { return pr{ a, b } += v; } pr operator - (pr const& v) const { return pr{ a, b } -= v; } pr operator - () const { return pr{ -a, -b }; } template operator pr() const { return { C(a), D(b) }; } template auto operator * (T const& v) const -> pr { return { x * v, y * v }; } template auto operator / (T const& v) const -> pr { return { x / v, y / v }; } template pr& operator *= (T const& v) { x *= v; y *= v; return *this; } template pr& operator /= (T const& v) { x /= v; y /= v; return *this; } void flip() { swap(x, y); } friend istream& operator>>(istream& is, pr& p) { is >> p.a >> p.b; return is; } friend ostream& operator<<(ostream& os, pr const& p) { os << p.a << " " << p.b; return os; } }; using pint = pr; using pdouble = pr; static_assert(is_pod::value, "not pod"); template struct arr : public vector { arr() {} arr(initializer_list il) : vector(il) {} explicit arr(int n, T v = T()) : vector(n, v) {} T& operator()(int i) { return (*this)[i]; } T const& operator()(int i) const { return (*this)[i]; } void init(int n, T v = T()) { this->assign(n, v); } int sz() const { return (int)this->size(); } void pb(T v) { this->push_back(v); } void sort() { std::sort(this->begin(), this->end()); } template void sort(F&& f) { std::sort(this->begin(), this->end(), f); } void rsort() { std::sort(this->begin(), this->end(), greater()); } void reverse() { std::reverse(this->begin(), this->end()); } void unique_erase() { this->erase(std::unique(this->begin(), this->end()), this->end()); } bool next_permutation() { return std::next_permutation(this->begin(), this->end()); } int lower_bound(T const& v, function p) { return std::lower_bound(this->begin(), this->end(), v, p) - this->begin(); } int lower_bound(T const& v) { return std::lower_bound(this->begin(), this->end(), v) - this->begin(); } int upper_bound(T const& v, function p) { return std::upper_bound(this->begin(), this->end(), v, p) - this->begin(); } int upper_bound(T const& v) { return std::upper_bound(this->begin(), this->end(), v) - this->begin(); } int find_sorted_nearest(T const& v) { int i = this->lower_bound(v); if (i >= sz()) { --i; } else if ((*this)[i] != v) { int p = i - 1; if (p >= 0) { int id = abs((*this)[i] - v); int pd = abs((*this)[p] - v); if (pd < id) { i = p; } } } return i; } int find_sorted(T const& v) { int i = this->lower_bound(v); if (i >= sz()) { return -1; } if ((*this)[i] != v) { return -1; } return i; } int find(T const& v) const { auto it = std::find(this->begin(), this->end(), v); if (it == this->end()) { return -1; } return it - this->begin(); } bool is_contains(T const& v) const { auto it = std::find(this->begin(), this->end(), v); if (it == this->end()) { return false; } return true; } template void remove_if_erase(P&& predicate) { this->erase(std::remove_if(this->begin(), this->end(), predicate), this->end()); } T& emplace_back_ref() { this->emplace_back(); return this->back(); } }; using ints = arr; template struct arr2 { vector> m_vec; int m_width; int m_height; arr2() : m_width(0), m_height(0) {} arr2(int w, int h, T const& value = T()) : m_width(w), m_height(h) { m_vec.resize(h, vector(w, value)); } arr2(arr2 const& r) { m_vec = r.m_vec; m_width = r.m_width; m_height = r.m_height; } arr2(arr2&& r) { m_vec = move(r.m_vec); m_width = r.m_width; m_height = r.m_height; } arr2& operator=(arr2 const& r) { m_vec = r.m_vec; m_width = r.m_width; m_height = r.m_height; return *this; } arr2& operator=(arr2&& r) { m_vec = move(r.m_vec); m_width = r.m_width; m_height = r.m_height; return *this; } bool operator ==(arr2 const& r) const { return m_vec = r.m_vec; } bool operator <(arr2 const& r) const { if (m_width != r.m_width) { return m_width < r.m_width; } if (m_height != r.m_height) { return m_height < r.m_height; } REP(y, m_height) { REP(x, m_width) { if ((*this)(x, y) != r(x, y)) { return (*this)(x, y) < r(x, y); } } } return false; } pint size() const { return pint{ m_width, m_height }; } int width() const { return m_width; } int height() const { return m_height; } void clear() { m_vec.clear(); m_width = 0; m_height = 0; } void init(int w, int h, T const& value = T()) { m_vec.clear(); m_vec.resize(h, vector(w, value)); m_width = w; m_height = h; } void init(pint size, T const& value = T()) { init(size.x, size.y, value); } T& operator()(int x, int y) { return m_vec[y][x]; } T const& operator()(int x, int y) const { return m_vec[y][x]; } T& operator()(pint p) { return m_vec[p.y][p.x]; } T const& operator()(pint p) const { return m_vec[p.y][p.x]; } T& operator[](pint p) { return m_vec[p.y][p.x]; } T const& operator[](pint p) const { return m_vec[p.y][p.x]; } vector& operator[](int row) { return m_vec[row]; } vector const& operator[](int row) const { return m_vec[row]; } bool isIn(int x, int y) const { return x >= 0 && x < m_width&& y >= 0 && y < m_height; } bool isIn(pint p) const { return isIn(p.x, p.y); } bool isOut(int x, int y) const { return x < 0 || x >= m_width || y < 0 || y >= m_height; } bool isOut(pint p) const { return isOut(p.x, p.y); } void disp(ostream& os, int w = 2) { REP(y, m_height) { REP(x, m_width) { os << setw(w) << (*this)(x, y) << " "; } os << endl; } os << endl; } }; template class CapacityArray { private: array array_ = {}; int count_ = 0; public: inline void Clear() { count_ = 0; } inline void Resize(int count) { count_ = count; } inline T* PushBack() { return &array_[count_++]; } inline void PushBack(const T& e) { array_[count_++] = e; } inline void PopBack() { --count_; } inline void RemoveSlide(int i) { array_[i] = array_[count_ - 1]; --count_; } inline int GetCount() const { return count_; } inline int size() const { return count_; } inline bool empty() const { return count_ == 0; } inline T& operator[](int index) { return array_[index]; } inline const T& operator[](int index) const { return array_[index]; } inline auto begin() -> decltype(array_.begin()) { return array_.begin(); } inline auto end() -> decltype(array_.begin()) { return array_.begin() + count_; } inline auto begin() const -> decltype(array_.begin()) { return array_.begin(); } inline auto end() const -> decltype(array_.begin()) { return array_.begin() + count_; } inline bool IsContain(const T& value) const { for (const auto& v : *this) { if (v == value) { return true; } } return false; } inline void MemOverride(int dstIndex, int dstCount, const T* srcPtr, int srcCount) { memmove(&array_[dstIndex + srcCount], &array_[dstIndex + dstCount], sizeof(T) * (count_ - (dstIndex + dstCount))); memcpy(&array_[dstIndex], srcPtr, sizeof(T) * srcCount); count_ += srcCount - dstCount; } }; template struct CapacitySet { private: CapacityArray elemens; array indexTable; public: CapacitySet() { fill(indexTable.begin(), indexTable.end(), -1); } void Clear() { elemens.Clear(); fill(indexTable.begin(), indexTable.end(), -1); } inline void Add(int ai) { indexTable[ai] = elemens.GetCount(); elemens.PushBack(ai); } inline void ForceAdd(int ai) { if (indexTable[ai] >= 0) { return; } indexTable[ai] = elemens.GetCount(); elemens.PushBack(ai); } inline void Remove(int ai) { int removeIndex = indexTable[ai]; int lastIndex = elemens.GetCount() - 1; if (removeIndex != lastIndex) { elemens[removeIndex] = elemens[lastIndex]; indexTable[elemens[lastIndex]] = removeIndex; } elemens.PopBack(); indexTable[ai] = -1; } inline void ForceRemove(int ai) { if (indexTable[ai] < 0) { return; } int removeIndex = indexTable[ai]; int lastIndex = elemens.GetCount() - 1; if (removeIndex != lastIndex) { elemens[removeIndex] = elemens[lastIndex]; indexTable[elemens[lastIndex]] = removeIndex; } elemens.PopBack(); indexTable[ai] = -1; } inline bool IsContain(int ai) const { return indexTable[ai] >= 0; } inline int GetCount() const { return elemens.GetCount(); } inline int size() const { return elemens.GetCount(); } inline int At(int index) const { return elemens[index]; } inline int operator[](int index) const { return elemens[index]; } inline auto begin() -> decltype(elemens.begin()) { return elemens.begin(); } inline auto end() -> decltype(elemens.begin()) { return elemens.end(); } inline auto begin() const -> decltype(elemens.begin()) { return elemens.begin(); } inline auto end() const -> decltype(elemens.begin()) { return elemens.end(); } }; struct SizeSet { private: vector elemens; vector indexTable; public: void InitSize(int size) { elemens.clear(); elemens.reserve(size); indexTable.assign(size, -1); } void Clear() { elemens.clear(); indexTable.assign(indexTable.size(), -1); } inline void Add(int ai) { indexTable[ai] = (int)elemens.size(); elemens.emplace_back(ai); } inline void ForceAdd(int ai) { if (indexTable[ai] >= 0) { return; } indexTable[ai] = (int)elemens.size(); elemens.emplace_back(ai); } inline void Remove(int ai) { int removeIndex = indexTable[ai]; int lastIndex = (int)elemens.size() - 1; if (removeIndex != lastIndex) { elemens[removeIndex] = elemens[lastIndex]; indexTable[elemens[lastIndex]] = removeIndex; } elemens.pop_back(); indexTable[ai] = -1; } inline void ForceRemove(int ai) { if (indexTable[ai] < 0) { return; } int removeIndex = indexTable[ai]; int lastIndex = (int)elemens.size() - 1; if (removeIndex != lastIndex) { elemens[removeIndex] = elemens[lastIndex]; indexTable[elemens[lastIndex]] = removeIndex; } elemens.pop_back(); indexTable[ai] = -1; } inline bool IsContain(int ai) const { return indexTable[ai] >= 0; } inline int GetCount() const { return (int)elemens.size(); } inline int At(int index) const { return elemens[index]; } inline int operator[](int index) const { return elemens[index]; } inline auto begin() -> decltype(elemens.begin()) { return elemens.begin(); } inline auto end() -> decltype(elemens.begin()) { return elemens.end(); } inline auto begin() const -> decltype(elemens.begin()) { return elemens.begin(); } inline auto end() const -> decltype(elemens.begin()) { return elemens.end(); } }; struct CheckMap { private: vector checked_; u32 mark_ = 0; public: void SetSize(int size) { checked_.resize(size, mark_); } void Clear() { ++mark_; if (mark_ == 0) { checked_.assign(checked_.size(), 0); ++mark_; } } bool IsChecked(int i) const { return checked_[i] == mark_; } void Check(int i) { checked_[i] = mark_; } }; template struct CheckMapData { private: vector data_; vector checked_; u32 mark_ = 0; public: void SetSize(int size) { checked_.resize(size, mark_); data_.resize(size); } void Clear() { ++mark_; if (mark_ == 0) { checked_.assign(checked_.size(), 0); ++mark_; } } bool IsChecked(int i) const { return checked_[i] == mark_; } void Check(int i) { checked_[i] = mark_; } void Set(int i, const T& value) { checked_[i] = mark_; data_[i] = value; } void Reset(int i) { checked_[i] = mark_ - 1; } const T& Get(int i) const { return data_[i]; } T& Ref(int i) { return data_[i]; } }; struct BiasDistribution { discrete_distribution dist_; template void set(ITERATOR&& begin, ITERATOR&& end) { dist_.param(discrete_distribution::param_type(begin, end)); } template int operator()(ENGINE& engine) { return dist_(engine); } }; #include struct XorShift { uint32_t x; uint32_t y; uint32_t z; uint32_t w; inline XorShift(uint32_t seed = 0) { x = 123456789; y = 362436069; z = 521288629; w = 88675123 + seed; } inline uint32_t operator()() { uint32_t t = x ^ (x << 11); x = y; y = z; z = w; return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); } inline uint32_t operator()(uint32_t l, uint32_t r) { return ((*this)() % (r - l)) + l; } inline uint32_t operator()(uint32_t r) { return (*this)() % r; } }; struct Xor64 { uint64_t x; inline Xor64(uint64_t seed = 0) { x = 88172645463325252ULL + seed; } inline uint64_t operator()() { x = x ^ (x << 7); return x = x ^ (x >> 9); } inline uint64_t operator()(uint64_t l, uint64_t r) { return ((*this)() % (r - l)) + l; } inline uint64_t operator()(uint64_t r) { return (*this)() % r; } }; struct Xor32 { using result_type = uint32_t; static constexpr result_type min() { return 0; } static constexpr result_type max() { return UINT32_MAX; } uint32_t x; inline Xor32(uint32_t seed = 0) { x = 2463534242 + seed; } inline uint32_t operator ()(void) { x = x ^ (x << 13); x = x ^ (x >> 17); return x = x ^ (x << 5); } inline uint32_t operator()(uint32_t l, uint32_t r) { return ((*this)() % (r - l)) + l; } inline uint32_t operator()(uint32_t r) { return (*this)() % r; } }; struct Random { static inline Xor32& Instnce() { static Xor32 x; return x; } static inline uint32_t Get() { return Instnce()(); } static inline uint32_t Get(uint32_t l, uint32_t r) { return Instnce()(l, r); } static inline uint32_t Get(uint32_t r) { return Instnce()(r); } static inline void Get2(uint32_t N, uint32_t& a, uint32_t& b) { } static inline double GetDouble() { return Get() / (double)UINT32_MAX; } }; #define REGIST_PARAM(name, type, defaultValue) type name = defaultValue; constexpr struct { REGIST_PARAM(ESTIMATE_STEP_COUNT, int, 29); REGIST_PARAM(EstimatePhaseTurn, int, 1); REGIST_PARAM(EstimatePhaseGeta, int, 248); REGIST_PARAM(EstimateMoveWidth, int, 24); REGIST_PARAM(AreaSpawnBaseRate, double, 1.926734588287346); REGIST_PARAM(AreaSpawnCenterB, double, 0.9028711031473236); REGIST_PARAM(AreaSpawnCenterC, double, 0.944961464534612); REGIST_PARAM(ExpectValuePlayerCountA, double, 2.6414616524112673); REGIST_PARAM(ExpectValuePlayerCountE, double, 0.41446193496664735); REGIST_PARAM(ExpectValueTurnA, double, 0.9610466431184533); REGIST_PARAM(ExpectValueTurnE, double, 1.3373020602261079); REGIST_PARAM(ExpectValueRangeA, double, 3.584080512244427); REGIST_PARAM(ExpectValueRangeE, double, 2.0993163643741855); REGIST_PARAM(GroupEvalCenterA, double, 0.01434715575105959); REGIST_PARAM(GroupEvalCenterE, double, 0.9219452669424091); REGIST_PARAM(GroupEvalCenterMaxA, double, 0.055399574807733616); REGIST_PARAM(GroupEvalCenterMaxE, double, 0.9956463142207564); REGIST_PARAM(GroupEvalPlayerCountA, double, 4.295776030147873); REGIST_PARAM(GroupEvalPlayerCountE, double, 1.4069726444736887); REGIST_PARAM(GroupEvalRangeA, double, 1.7999320241999943); REGIST_PARAM(GroupEvalRangeE, double, 1.4356064609862071); REGIST_PARAM(GroupRelationCrossA, double, 19.07975485213188); REGIST_PARAM(GroupRelationCrossE, double, 0.8934338608074761); } HYPER_PARAM; constexpr int T_MAX = 3600; constexpr int R_MAX = 4; constexpr int N_MAX = 5400; constexpr int S_MIN = 0; constexpr int S_MAX = 100; constexpr int S_CENTER = 50; constexpr int PlayerCountBonusTable[4] = { 0, 1, 3, 6 }; struct UnionFind { struct Node { Node* parent = nullptr; int count = 1; Node* Root() { if (parent == nullptr) { return this; } return parent = parent->Root(); } }; vector nodes; int groupCount; UnionFind(int count) { nodes.resize(count); groupCount = count; } UnionFind() { } void Init(int count) { nodes.resize(count); groupCount = count; } void Join(int a, int b) { Node* rootA = nodes[a].Root(); Node* rootB = nodes[b].Root(); if (rootA == rootB) { return; } if (rootA->count > rootB->count) { swap(rootA, rootB); } rootB->count += rootA->count; rootA->parent = rootB; rootA->count = 0; --groupCount; } bool IsReachable(int a, int b) { Node* rootA = nodes[a].Root(); Node* rootB = nodes[b].Root(); return rootA == rootB; } int Count(int a) { return nodes[a].Root()->count; } int GetGroupCount() const { return groupCount; } vector> Groups() { std::map> m; for (int i = 0; i < (int)nodes.size(); ++i) { auto& node = nodes[i]; m[node.Root()].push_back(i); } vector> groups(m.size()); int i = 0; for (auto& p : m) { groups[i++] = std::move(p.second); } return groups; } }; struct Player { int startTurn; int skill; }; struct IOServer { int T; int R; int turn; arr players; arr newPlayers; void Init() { istream& is = cin; is >> T >> R; turn = 0; players.reserve(N_MAX); InputNext(); } private: void InputNext() { istream& is = cin; if (turn >= T) { newPlayers.clear(); return; } int n; int S; is >> n; newPlayers.resize(n); REP(i, n) { is >> S; auto& player = players.emplace_back_ref(); player.skill = S; player.startTurn = turn; newPlayers[i] = (int)players.size() - 1; } } public: void TurnOutput(const arr& joinPlayers) { ostream& os = cout; os << joinPlayers.size() << endl; REP(i, joinPlayers.size()) { const auto& j = joinPlayers[i]; os << j.a + 1 << " " << j.b + 1 << endl; } ++turn; InputNext(); } int CalcScore() const { return 0; } }; IOServer server; constexpr double SkillSpawnRate[101] = { 0.000886591 , 0.00100397 , 0.001133689 , 0.001277618 , 0.001434525 , 0.00160503 , 0.001795888 , 0.002001511 , 0.00222116 , 0.002468785 , 0.0027315 , 0.003015067 , 0.003319959 , 0.003645566 , 0.003992201 , 0.004363595 , 0.004758201 , 0.005177994 , 0.005611699 , 0.006068417 , 0.006552535 , 0.007050818 , 0.007574194 , 0.008116218 , 0.008667253 , 0.009239319 , 0.009819463 , 0.010409084 , 0.01102259 , 0.011625182 , 0.012244323 , 0.012851716 , 0.013460935 , 0.014057413 , 0.014651523 , 0.015233315 , 0.015801684 , 0.016338302 , 0.016855305 , 0.017346859 , 0.01781184 , 0.018233642 , 0.018635775 , 0.018968129 , 0.019287902 , 0.019553354 , 0.019790864 , 0.019950125 , 0.020078118 , 0.02015592 , 0.020183211 , 0.020152242 , 0.020079253 , 0.019953895 , 0.019775677 , 0.019562781 , 0.019289282 , 0.018976212 , 0.018634235 , 0.018226315 , 0.017805866 , 0.017336229 , 0.016854305 , 0.016339981 , 0.015797902 , 0.01523896 , 0.014654296 , 0.014062827 , 0.013465667 , 0.012854553 , 0.012237758 , 0.011632685 , 0.011023942 , 0.010418014 , 0.009827223 , 0.009239522 , 0.008669362 , 0.008111972 , 0.007575837 , 0.007053683 , 0.00654921 , 0.006070975 , 0.005610849 , 0.005174682 , 0.004759192 , 0.00436347 , 0.003991778 , 0.003646986 , 0.003321075 , 0.00301742 , 0.002731122 , 0.002468613 , 0.002225941 , 0.00200128 , 0.001796238 , 0.001606176 , 0.001433477 , 0.001276184 , 0.001133847 , 0.001003772 , 0.00088736 , }; #define VALIDATE_SCORE 0 #define VALIDATE_PHASE_ESTIMATE 0 #define GROUP_SCORE_CHECK 0 struct Group { CapacityArray players; int minSkill; int maxSkill; int matchingTurnSum; int startTurnSum; bool IsEmpty() const { return players.empty(); } bool IsFull() const { return players.size() == R_MAX; } int CenterDist() const { if (S_CENTER >= minSkill && S_CENTER <= maxSkill) { return 0; } return min(abs(minSkill - S_CENTER), abs(maxSkill - S_CENTER)); } int CenterDistMax() const { if (S_CENTER >= minSkill && S_CENTER <= maxSkill) { return 0; } return max(abs(minSkill - S_CENTER), abs(maxSkill - S_CENTER)); } int GetSkillRangeSize() const { return maxSkill - minSkill + 1; } int CalcScore() const { int ps = matchingTurnSum - (players.size() - 1) * startTurnSum; int ce = 0; if (players.size() == 2) { ce = 1; } else if (players.size() == 3) { ce = 3; } else if (players.size() == 4) { ce = 6; } int score = max(ce * (200 - square(maxSkill - minSkill)) - ps, 0); return score; } int CalcScoreIfAddPlayer(int skill, int nowTurn, int startTurn) const { int newMatchingTurnSum = matchingTurnSum + 2 * players.size() * nowTurn; int newStartTurnSum = startTurnSum + startTurn; int newPlayerCount = players.size() + 1; int newMinSkill = min(minSkill, skill); int newMaxSkill = max(maxSkill, skill); int ps = newMatchingTurnSum - (newPlayerCount - 1) * newStartTurnSum; int ce = 0; if (newPlayerCount == 2) { ce = 1; } else if (newPlayerCount == 3) { ce = 3; } else if (newPlayerCount == 4) { ce = 6; } int score = max(ce * (200 - square(newMaxSkill - newMinSkill)) - ps, 0); return score; } void Clear() { players.Clear(); } void InitGroup(int pi, int skill, int startTurn) { matchingTurnSum = 0; startTurnSum = startTurn; players.PushBack(pi); minSkill = maxSkill = skill; } void AddPlayer(int pi, int skill, int nowTurn, int startTurn) { matchingTurnSum += 2 * players.size() * nowTurn; startTurnSum += startTurn; players.PushBack(pi); if (skill < minSkill) { minSkill = skill; } else if (skill > maxSkill) { maxSkill = skill; } } void JoinGroup(Group& group, int nowTurn) { matchingTurnSum += group.matchingTurnSum; matchingTurnSum += 2 * players.size() * group.players.size() * nowTurn; startTurnSum += group.startTurnSum; if (players[0] < group.players[0]) { for (int pi : group.players) { players.PushBack(pi); } } else { for (int pi : players) { group.players.PushBack(pi); } players = group.players; } if (group.minSkill < minSkill) { minSkill = group.minSkill; } if (group.maxSkill > maxSkill) { maxSkill = group.maxSkill; } group.Clear(); } }; struct PhaseEstimater { struct Param { int moveWidth = 0; double B = 0; double C = 0; double F = 0; double G = 0; double H = 0; int sampleCount = 0; int moveTotal_ = 0; int moveCount_ = 0; void SetMoveWidth(int mw) { moveWidth = mw; } double CalcE(double np) const { return B / 2.0 * sin(2 * np) - C / 2.0 * cos(2 * np) - 2 * F * cos(np) - 2 * G * sin(np) + H + (sampleCount) / 2.0; } double CalcDiff(double np) const { return B * cos(2 * np) + C * sin(2 * np) + 2 * F * sin(np) - 2 * G * cos(np); } double CalcDiff2(double np) const { return -B * sin(2 * np) + C * cos(2 * np) + 2 * F * cos(np) + 2 * G * sin(np); } void Add(int turn, int spawnCount, const arr& spawnCountsRecord) { moveTotal_ += spawnCount; ++moveCount_; if (moveCount_ > moveWidth) { --moveCount_; moveTotal_ -= spawnCountsRecord[spawnCountsRecord.sz() - moveWidth - 1]; } double avgSpawnCpunt = moveTotal_ / (double)moveCount_; double y = avgSpawnCpunt - 1.5; double x = (turn - (moveWidth - 1) / 2.0) * 2 * M_PI / (double)T_MAX; B += sin(2 * x); C += cos(2 * x); F += y * sin(x); G += y * cos(x); H += y * y; ++sampleCount; } }; arr params_; double phase = 0; arr spawnCounts_; int spawnTotal_ = 0; PhaseEstimater() { spawnCounts_.reserve(T_MAX); arr WS = { HYPER_PARAM.EstimateMoveWidth }; params_.resize(WS.size()); REP(i, WS.size()) { params_[i].SetMoveWidth(WS[i]); }; } void Add(int turn, int spawnCount) { spawnCounts_.emplace_back(spawnCount); spawnTotal_ += spawnCount; for (auto& param : params_) { param.Add(turn, spawnCount, spawnCounts_); } const auto& paramW1 = params_[0]; double minNp = 0; double minError = INT_MAX; int usedWidth = -1; for (auto& param : params_) { constexpr int TryCount = 4; REP(i, TryCount) { double np = phase * 2 * M_PI / (double)T_MAX + (i * 2 * M_PI / (double)TryCount); REP(step, HYPER_PARAM.ESTIMATE_STEP_COUNT) { double dE = param.CalcDiff(np); double d2E = param.CalcDiff2(np); np = np - dE / d2E; } double baseError = paramW1.CalcE(np); { double flipError = paramW1.CalcE(np + M_PI); if (baseError > flipError) { np += M_PI; baseError = flipError; } } if (baseError < minError) { minNp = np; minError = baseError; usedWidth = param.moveWidth; } } } double np = minNp; phase = np * T_MAX / (2 * M_PI); while (phase > T_MAX) { phase -= T_MAX; } while (phase < 0) { phase += T_MAX; } } double CalcTurnSpawnCount(int turn) const { double turnSpwanCount = 1.5; if (server.turn > 0 && server.turn < HYPER_PARAM.EstimatePhaseTurn) { turnSpwanCount = (spawnTotal_ + 1.5 * HYPER_PARAM.EstimatePhaseGeta) / (double)(server.turn + HYPER_PARAM.EstimatePhaseGeta); chmin(turnSpwanCount, 2.5); chmax(turnSpwanCount, 0.5); } else { constexpr double DIV = 1.0 / (double)T_MAX * 2.0 * M_PI; turnSpwanCount = 1.5 + sin((turn + phase) * DIV); } return turnSpwanCount; } double CalcAverageSpawnCount() const { return spawnTotal_ / (double)spawnCounts_.size(); } double CalcMoveAverageSpawnCount() const { const auto& param = params_.back(); return param.moveTotal_ / (double)param.moveCount_; } double CalcError() const { constexpr double DIV = 1.0 / (double)T_MAX * 2.0 * M_PI; double totalError = 0; REP(turn, spawnCounts_.size()) { double turnSpwanCount = 1.5 + sin((turn + phase) * DIV); double err = square(spawnCounts_[turn] - turnSpwanCount); totalError += err; } totalError /= (double)spawnCounts_.sz(); totalError = sqrt(totalError); return totalError; } }; double ParamValue(double x, double a, double e) { return a * pow(x, e); } struct Solver { array, S_MAX + 1> playerQueue_; array groups_; PhaseEstimater phaseEstimater_; void FinishGroup(Group& group) { group.Clear(); } void Run() { arr joinPlayers; REP(t, server.T) { for (int pi : server.newPlayers) { playerQueue_[server.players[pi].skill].push(pi); } phaseEstimater_.Add(t, server.newPlayers.sz()); joinPlayers.clear(); RemoveScoreDownGroup(); JoinOnGroup(joinPlayers); AssignNewGroup(joinPlayers); JoinGroup5(joinPlayers); server.TurnOutput(joinPlayers); if (timer.IsOver()) { break; } } } void JoinGroup5(arr& joinPlayers) { arr groupOrder; REP(gi, S_MAX + 1) { auto& group = groups_[gi]; if (group.IsEmpty()) { continue; } groupOrder.emplace_back(gi); } vector> scoreTable(groupOrder.sz(), vector(groupOrder.sz(), -1)); vector> rangeTable(groupOrder.sz(), vector(groupOrder.sz(), pint{ -1, -1 })); auto SetTable = [&](const Group& group, const pint& limitRange, int startOi, int curOi) { pint maxRange = { -1, -1 }; double score = CalcMaxValueRange(group, maxRange, limitRange); VASSERT(maxRange.a >= 0 && maxRange.b >= 0); { score -= ParamValue(group.CenterDist(), HYPER_PARAM.GroupEvalCenterA, HYPER_PARAM.GroupEvalCenterE); score -= ParamValue(group.CenterDistMax(), HYPER_PARAM.GroupEvalCenterMaxA, HYPER_PARAM.GroupEvalCenterMaxE); score += ParamValue(group.players.size(), HYPER_PARAM.GroupEvalPlayerCountA, HYPER_PARAM.GroupEvalPlayerCountE); score -= ParamValue(maxRange.b - maxRange.a, HYPER_PARAM.GroupEvalRangeA, HYPER_PARAM.GroupEvalRangeE); if (score < 0) { score = 0; } } score *= group.players.size(); scoreTable[startOi][curOi] = score; rangeTable[startOi][curOi] = maxRange; }; #define ENABLE_HALF_LIMIT 0 REP(startOi, groupOrder.size()) { int startGi = groupOrder[startOi]; Group group = groups_[startGi]; pint limitRange; if (startOi > 0) { limitRange.a = groups_[groupOrder[startOi - 1]].maxSkill + 1; } else { limitRange.a = 0; } if (startOi + 1 < groupOrder.sz()) { limitRange.b = groups_[groupOrder[startOi + 1]].minSkill - 1; } else { limitRange.b = S_MAX; } SetTable(group, limitRange, startOi, startOi); FOR(curOi, startOi + 1, groupOrder.size()) { int curGi = groupOrder[curOi]; if (group.players.size() + groups_[curGi].players.size() > R_MAX) { break; } Group tmp = groups_[curGi]; group.JoinGroup(tmp, server.turn); if (curOi + 1 < groupOrder.sz()) { limitRange.b = groups_[groupOrder[curOi + 1]].minSkill - 1; } else { limitRange.b = S_MAX; } SetTable(group, limitRange, startOi, curOi); } } double maxScore = -1; arr maxJoinStack; { arr joinStack; auto DFS = [&](auto&& dfs, const pint& prevRange, s8 startOi, s8 oi, double totalScore) { if (oi >= groupOrder.sz()) { if (totalScore > maxScore) { maxScore = totalScore; maxJoinStack = joinStack; } return; } double score = scoreTable[startOi][oi]; if (score < 0) { return; } { double rangeScore = 0; const pint& range = rangeTable[startOi][oi]; if (range.a <= prevRange.b) { int crossCount = prevRange.b - range.a + 1; rangeScore -= ParamValue(crossCount, HYPER_PARAM.GroupRelationCrossA, HYPER_PARAM.GroupRelationCrossE); } joinStack.emplace_back(oi); dfs(dfs, range, oi + 1, oi + 1, totalScore + score + rangeScore); joinStack.pop_back(); } if (score > 0) { dfs(dfs, prevRange, startOi, oi + 1, totalScore); } }; DFS(DFS, pint{ -1, -1 }, 0, 0, 0); } s8 startOi = 0; for (int oi : maxJoinStack) { if (startOi != oi) { auto& group1 = groups_[groupOrder[startOi]]; FOR(oi2, startOi + 1, oi + 1) { auto& group2 = groups_[groupOrder[oi2]]; VASSERT(group1.players.size() + group2.players.size() <= R_MAX); joinPlayers.emplace_back(pint{ group1.players[0], group2.players[0] }); group1.JoinGroup(group2, server.turn); } if (group1.IsFull()) { FinishGroup(group1); } } startOi = oi + 1; } } double CalcMaxValue(const Group& group, int siL, int siR) const { double maxScore = -1; auto CheckMaxScore = [&](const Group& tmpGroup, int turn) { double score = tmpGroup.CalcScore(); { score += ParamValue(tmpGroup.players.size(), HYPER_PARAM.ExpectValuePlayerCountA, HYPER_PARAM.ExpectValuePlayerCountE); score -= ParamValue(turn - server.turn, HYPER_PARAM.ExpectValueTurnA, HYPER_PARAM.ExpectValueTurnE); score -= ParamValue(tmpGroup.maxSkill - tmpGroup.minSkill, HYPER_PARAM.ExpectValueRangeA, HYPER_PARAM.ExpectValueRangeE); if (score < 0) { score = 0; } } if (server.turn < T_MAX - 1) { score /= tmpGroup.players.size(); } if (score > maxScore) { maxScore = score; } return score; }; CheckMaxScore(group, server.turn); if (!group.IsFull()) { auto tmpGroup = group; tmpGroup.minSkill = siL; tmpGroup.maxSkill = siR; double spawnRate = 0; FOR(si, siL, siR + 1) { spawnRate += SkillSpawnRate[si]; } spawnRate *= HYPER_PARAM.AreaSpawnBaseRate; spawnRate *= pow(HYPER_PARAM.AreaSpawnCenterB, tmpGroup.CenterDist() / 50.0); spawnRate *= pow(HYPER_PARAM.AreaSpawnCenterC, tmpGroup.CenterDistMax() / 50.0); double power = 0; FOR(turn, server.turn + 1, server.T) { double turnSpwanCount = phaseEstimater_.CalcTurnSpawnCount(turn); power += turnSpwanCount * spawnRate; if (power >= 1.0) { power -= 1.0; tmpGroup.AddPlayer(-1, siL, turn, turn); CheckMaxScore(tmpGroup, turn); if (tmpGroup.IsFull()) { break; } } } } return maxScore; } double CalcMaxValueRange(const Group& group, pint& maxRange, const pint& limitRange) const { int siL = group.minSkill; int siR = group.maxSkill; int maxSiL = -1; int maxSiR = -1; double maxScore = -1; while (true) { double score = CalcMaxValue(group, siL, siR); if (score > maxScore) { maxScore = score; maxSiL = siL; maxSiR = siR; } if (siR - siL >= 14) { break; } CapacityArray candidates; if (siL - 1 >= 0 && siL - 1 >= limitRange.a) { candidates.PushBack(siL - 1); } if (siR + 1 <= S_MAX && siR + 1 <= limitRange.b) { candidates.PushBack(siR + 1); } if (candidates.GetCount() == 0) { break; } int newSi = -1; if (candidates.GetCount() == 2) { if (SkillSpawnRate[candidates[0]] >= SkillSpawnRate[candidates[1]]) { newSi = candidates[0]; } else { newSi = candidates[1]; } } else { newSi = candidates[0]; } if (newSi == siL - 1) { --siL; } else { ++siR; } } maxRange.a = maxSiL; maxRange.b = maxSiR; return maxScore; } void RemoveScoreDownGroup() { REP(gi, S_MAX + 1) { auto& group = groups_[gi]; if (group.IsEmpty()) { continue; } double curScore = group.CalcScore(); double newScore = group.CalcScoreIfAddPlayer(group.minSkill, server.turn, server.turn); curScore /= group.players.size(); newScore /= group.players.size() + 1; if (newScore <= curScore) { FinishGroup(group); } } } void JoinOnGroup(arr& joinPlayers) { REP(gi, S_MAX + 1) { auto& group = groups_[gi]; if (group.IsEmpty()) { continue; } while (true) { int outerSi = -1; int outerDist = -1; FOR(si, group.minSkill, group.maxSkill + 1) { if (playerQueue_[si].empty()) { continue; } int dist = abs(si - S_CENTER); if (dist > outerDist) { outerDist = dist; outerSi = si; } } if (outerSi < 0) { break; } int pi = playerQueue_[outerSi].front(); playerQueue_[outerSi].pop(); joinPlayers.emplace_back(pint{ group.players[0], pi }); group.AddPlayer(pi, server.players[pi].skill, server.turn, server.players[pi].startTurn); if (group.IsFull()) { FinishGroup(group); break; } } } } void AssignNewGroup(arr& joinPlayers) { REP(si, S_MAX + 1) { auto& queue = playerQueue_[si]; while (!queue.empty()) { int pi = queue.front(); queue.pop(); auto& group = groups_[si]; group.InitGroup(pi, server.players[pi].skill, server.players[pi].startTurn); REP(loop, R_MAX - 1) { if (queue.empty()) { break; } int pi = queue.front(); queue.pop(); joinPlayers.emplace_back(pint{ group.players[0], pi }); group.AddPlayer(pi, server.players[pi].skill, server.turn, server.players[pi].startTurn); } if (group.IsFull()) { FinishGroup(group); } } } } }; struct Main { void Run(int argc, const char* argv[]) { server.Init(); timer.StartMs(TIME_LIMIT); Solver* solver = new Solver; solver->Run(); } };