提出 #68411442
ソースコード 拡げる
// AHC051 A - Probabilistic Waste Sorting
// S-snake + corridor pruning. Devices connect only from recovery lanes (lane%4 in {1,2}).
// If crossings are detected, retry with fewer lanes (wider lanes). Guaranteed valid fallback.
//
// Compile: g++ -O2 -std=gnu++17 main.cpp
#include <bits/stdc++.h>
using namespace std;
static const int WIDTH = 10000;
static const int HEIGHT = 10000;
// ================= Geometry =================
struct P{ int x,y; };
static inline int sgn(long long v){ return (v>0)-(v<0); }
static inline int orient(const P& a,const P& b,const P& c){
long long cr = 1LL*(b.x-a.x)*(c.y-a.y) - 1LL*(b.y-a.y)*(c.x-a.x);
return sgn(cr); // >0: left
}
static inline bool on_seg(const P& a,const P& b,const P& c){
return min(a.x,b.x)<=c.x && c.x<=max(a.x,b.x) &&
min(a.y,b.y)<=c.y && c.y<=max(a.y,b.y);
}
static inline bool bbox_disjoint(const P& p1,const P& p2,const P& q1,const P& q2){
if(max(p1.x,p2.x) < min(q1.x,q2.x)) return true;
if(max(q1.x,q2.x) < min(p1.x,p2.x)) return true;
if(max(p1.y,p2.y) < min(q1.y,q2.y)) return true;
if(max(q1.y,q2.y) < min(p1.y,p2.y)) return true;
return false;
}
static inline bool segments_intersect(const P& p1,const P& q1,const P& p2,const P& q2,bool allow_touch=true){
if(bbox_disjoint(p1,q1,p2,q2)) return false;
int o1=orient(p1,q1,p2), o2=orient(p1,q1,q2);
int o3=orient(p2,q2,p1), o4=orient(p2,q2,q1);
if(o1*o2<0 && o3*o4<0) return true;
if(!allow_touch){
if(o1==0 && on_seg(p1,q1,p2)) return true;
if(o2==0 && on_seg(p1,q1,q2)) return true;
if(o3==0 && on_seg(p2,q2,p1)) return true;
if(o4==0 && on_seg(p2,q2,p1)) return true;
}
return false;
}
static inline uint64_t key64(const P& a){ return (uint64_t(uint32_t(a.x))<<32) | uint32_t(a.y); }
// ================= Plan container =================
struct Plan{
vector<int> d; // device assignment (0..N-1 identity)
int s; // start destination node id
vector<string> lines; // M lines for splitters
vector<pair<P,P>> segments; // all conveyor segments for validation
bool ok = false;
};
// ---- reachability (for cycle check) ---------------------------------
static bool path_exists(const vector<vector<int>>& G, int s, int t){
if(s==t) return true;
int n=(int)G.size();
vector<char> vis(n,0);
std::stack<int> st; st.push(s); vis[s]=1;
while(!st.empty()){
int u=st.top(); st.pop();
for(int v:G[u]){
if(!vis[v]){
if(v==t) return true;
vis[v]=1; st.push(v);
}
}
}
return false;
}
// ---- add arc if valid: outdeg≤2 / no crossing / no cycle ------------
static bool add_arc_if_valid(
int u, int v,
const vector<P>& sor,
vector<vector<int>>& G,
vector<int>& outdeg,
vector<pair<P,P>>& all_segments
){
if(u==v) return false;
if(outdeg[u] >= 2) return false; // splitter 2本まで
if(path_exists(G, v, u)) return false; // u<-...-v が既にある → 閉路
const P pu = sor[u], pv = sor[v];
// 交差チェック(端点共有はOK)
auto same = [](const P& a,const P& b){ return a.x==b.x && a.y==b.y; };
for(const auto& e: all_segments){
const P& a=e.first; const P& b=e.second;
if( same(a,pu)||same(a,pv)||same(b,pu)||same(b,pv) ) continue;
if(segments_intersect(pu,pv,a,b,true)) return false;
}
// 採用
G[u].push_back(v);
outdeg[u]++;
all_segments.push_back({pu,pv});
return true;
}
// ---- 同一レーンを S字で向き付け & レーン間ブリッジ -------------------
static void add_snake_same_lane_and_bridges(
const vector<vector<int>>& lanes,
const vector<P>& sor,
vector<vector<int>>& G,
vector<int>& outdeg,
vector<pair<P,P>>& all_segments
){
const int B = (int)lanes.size();
// レーン i の並び(i 偶数: 左→右, i 奇数: 右→左)
vector<vector<int>> dir_ids(B);
for(int i=0;i<B;i++){
dir_ids[i] = lanes[i];
sort(dir_ids[i].begin(), dir_ids[i].end(),
[&](int a,int b){ return sor[a].x < sor[b].x; });
if(i%2==1) reverse(dir_ids[i].begin(), dir_ids[i].end()); // S字
}
// (1) 同一レーン内:隣接を i の方向に張る
for(int i=0;i<B;i++){
auto &ids = dir_ids[i];
for(int t=0;t+1<(int)ids.size();++t){
int u = ids[t], v = ids[t+1];
add_arc_if_valid(u, v, sor, G, outdeg, all_segments);
}
}
// (2) レーン間ブリッジ:i の末端 -> (i+1) の始端(同じ側)を優先して1本
for(int i=0;i+1<B;i++){
if(dir_ids[i].empty() || dir_ids[i+1].empty()) continue;
int u = dir_ids[i].back(); // レーン i の終点
int v = dir_ids[i+1].front(); // レーン i+1 の始点(折り返し)
add_arc_if_valid(u, v, sor, G, outdeg, all_segments);
}
}
// ================= Builder =================
struct Builder {
// ------- fields -------
int N, M, K, B;
const vector<P>& dev;
const vector<P>& sor;
const P START{0, 5000};
// ------- ctor -------
Builder(int N, int M, int K, int B,
const vector<P>& d, const vector<P>& s)
: N(N), M(M), K(K), B(B), dev(d), sor(s) {}
// ------- lane helpers -------
inline int band_y(int y) const {
int denom = max(1, HEIGHT / B);
int li = y / denom;
if (li < 0) li = 0;
if (li >= B) li = B - 1;
return li;
}
inline bool is_recovery(int li) const { return (li % 4) == 1 || (li % 4) == 2; }
private:
// ===== 層間ペア枝ユーティリティ =====
bool has_path_in_G_(int s, int t, const vector<vector<int>>& G) const {
vector<char> vis(M, 0);
stack<int> st;
st.push(s);
vis[s] = 1;
while (!st.empty()) {
int u = st.top(); st.pop();
if (u == t) return true;
for (int v : G[u]) if (!vis[v]) { vis[v] = 1; st.push(v); }
}
return false;
}
bool try_add_pair_edge_(int u, int v,
vector<vector<int>>& G, vector<int>& outdeg,
vector<pair<int,int>>& pair_edges,
vector<pair<P,P>>& all_segments) const
{
if (u == v) return false;
if (outdeg[u] >= 2) return false;
const P& pu = sor[u];
const P& pv = sor[v];
auto same = [](const P& a, const P& b){ return a.x == b.x && a.y == b.y; };
// 既存線分と交差しないか
for (const auto& e : all_segments) {
const P& a = e.first; const P& b = e.second;
if (same(a, pu) || same(a, pv) || same(b, pu) || same(b, pv)) continue;
if (segments_intersect(pu, pv, a, b, true)) return false;
}
// 追加済みペア枝とも交差しないか
for (const auto& pr : pair_edges) {
const P& a = sor[pr.first];
const P& b = sor[pr.second];
if (same(a, pu) || same(a, pv) || same(b, pu) || same(b, pv)) continue;
if (segments_intersect(pu, pv, a, b, true)) return false;
}
// 閉路を作らない(v→…→u があるなら u→v は不可)
if (has_path_in_G_(v, u, G)) return false;
// 追加
pair_edges.push_back({u, v});
G[u].push_back(v);
outdeg[u]++;
all_segments.push_back({pu, pv});
return true;
}
void add_adjacent_lane_pairs_(const vector<vector<int>>& lanes,
vector<vector<int>>& G, vector<int>& outdeg,
vector<pair<int,int>>& pair_edges,
vector<pair<P,P>>& all_segments) const
{
auto byX = [&](int a, int b){ return sor[a].x < sor[b].x; };
for (int li = 0; li + 1 < B; ++li) {
vector<int> A = lanes[li];
vector<int> Bn = lanes[li + 1];
sort(A.begin(), A.end(), byX);
sort(Bn.begin(), Bn.end(), byX);
int i = 0, j = 0, cntAB = 0, cntBA = 0;
while (i < (int)A.size() && j < (int)Bn.size()) {
int a = A[i], b = Bn[j];
bool okAB = try_add_pair_edge_(a, b, G, outdeg, pair_edges, all_segments);
bool okBA = try_add_pair_edge_(b, a, G, outdeg, pair_edges, all_segments);
if (okAB && okBA) {
// バランス優先(次点で距離短い方に偏る)
if (cntAB <= cntBA) {
// BA を巻き戻す
pair_edges.pop_back(); G[b].pop_back(); outdeg[b]--; all_segments.pop_back();
j++; cntAB++;
} else {
// AB を巻き戻す
pair_edges.pop_back(); G[a].pop_back(); outdeg[a]--; all_segments.pop_back();
i++; cntBA++;
}
} else if (okAB) {
j++; cntAB++;
} else if (okBA) {
i++; cntBA++;
} else {
// どちらも不可なら x の小さい方を進める
(sor[a].x <= sor[b].x) ? ++i : ++j;
}
}
}
}
public:
// ====== main builder ======
Plan build() {
Plan out;
out.d.resize(N);
iota(out.d.begin(), out.d.end(), 0);
// ---- lanes ----
vector<vector<int>> lanes(B);
for (int i = 0; i < M; i++) lanes[band_y(sor[i].y)].push_back(i);
for (int i = 0; i < B; i++)
sort(lanes[i].begin(), lanes[i].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
if (lanes[0].empty()) {
int best = 0;
for (int i = 1; i < M; i++) if (sor[i].y < sor[best].y) best = i;
lanes[0] = {best};
}
int p0_idx = lanes[0][0];
P p0 = sor[p0_idx];
// START->p0 の左側は使わない
auto left_of_start = [&](const P& c){
long long cr = 1LL * (p0.x - START.x) * (c.y - START.y)
- 1LL * (p0.y - START.y) * (c.x - START.x);
return cr < 0;
};
vector<char> valid(M, 1);
for (int i = 0; i < M; i++) if (left_of_start(sor[i])) valid[i] = 0;
// lanes を valid で組み直し
for (int i = 0; i < B; i++) lanes[i].clear();
for (int i = 0; i < M; i++) if (valid[i]) lanes[band_y(sor[i].y)].push_back(i);
for (int i = 0; i < B; i++)
sort(lanes[i].begin(), lanes[i].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
// ---- undirected skeleton with crossing guard ----
vector<pair<P,P>> undirected, crossing;
auto crosses_any = [&](const P& u, const P& v)->bool{
for (const auto& e : crossing) {
const P& a = e.first; const P& b = e.second;
if ((a.x == u.x && a.y == u.y) || (a.x == v.x && a.y == v.y) ||
(b.x == u.x && b.y == u.y) || (b.x == v.x && b.y == v.y)) continue;
if (segments_intersect(u, v, a, b, true)) return true;
}
return false;
};
auto add_u = [&](const P& u, const P& v){
if (u.x == v.x && u.y == v.y) return false;
if (crosses_any(u, v)) return false;
undirected.push_back({u, v});
crossing.push_back({u, v});
return true;
};
// start chain
add_u(START, p0);
if (!lanes[1].empty()) add_u(p0, sor[lanes[1][0]]);
auto is_right = [&](const P& A, const P& Z, const P& C){ return orient(A, Z, C) < 0; };
auto is_left = [&](const P& A, const P& Z, const P& C){ return orient(A, Z, C) > 0; };
// even groups: base=0,4,8,...(右端ブリッジ)
for (int base = 0; base < B; base += 4) {
if (base + 3 >= B) break;
if (lanes[base].empty() || lanes[base + 3].empty()) continue;
P A = sor[lanes[base].back()], Z = sor[lanes[base + 3].back()];
if (add_u(A, Z)) {
for (int L : {base + 1, base + 2}) {
vector<int> v;
for (int idx : lanes[L]) if (!is_right(A, Z, sor[idx])) v.push_back(idx);
lanes[L].swap(v);
}
if (!lanes[base + 1].empty() && !lanes[base + 2].empty()) {
int u = *max_element(lanes[base + 1].begin(), lanes[base + 1].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
int v = *max_element(lanes[base + 2].begin(), lanes[base + 2].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
P U = sor[u], V = sor[v];
if (add_u(U, V)) {
for (int L : {base + 1, base + 2}) {
vector<int> w;
for (int idx : lanes[L]) if (!is_right(U, V, sor[idx])) w.push_back(idx);
lanes[L].swap(w);
}
}
}
}
}
// odd groups: base=2,6,...(左端ブリッジ)
for (int base = 2; base < B; base += 4) {
if (base + 3 >= B) break;
if (lanes[base].empty() || lanes[base + 3].empty()) continue;
P A = sor[lanes[base].front()], Z = sor[lanes[base + 3].front()];
if (add_u(A, Z)) {
for (int L : {base + 1, base + 2}) {
vector<int> v;
for (int idx : lanes[L]) if (!is_left(A, Z, sor[idx])) v.push_back(idx);
lanes[L].swap(v);
}
if (!lanes[base + 1].empty() && !lanes[base + 2].empty()) {
int u = *min_element(lanes[base + 1].begin(), lanes[base + 1].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
int v = *min_element(lanes[base + 2].begin(), lanes[base + 2].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
P U = sor[u], V = sor[v];
if (add_u(U, V)) {
for (int L : {base + 1, base + 2}) {
vector<int> w;
for (int idx : lanes[L]) if (!is_left(U, V, sor[idx])) w.push_back(idx);
lanes[L].swap(w);
}
}
}
}
}
// 各レーン内の単調エッジ
for (int i = 0; i < B; i++) {
auto ids = lanes[i];
if ((int)ids.size() < 2) continue;
sort(ids.begin(), ids.end(), [&](int a, int b){ return sor[a].x < sor[b].x; });
for (int t = 0; t + 1 < (int)ids.size(); ++t) add_u(sor[ids[t]], sor[ids[t + 1]]);
}
// 最上段 2 レーンの連結
if (B >= 2 && !lanes[B - 2].empty() && !lanes[B - 1].empty()) {
int la = *min_element(lanes[B - 2].begin(), lanes[B - 2].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
int lb = *min_element(lanes[B - 1].begin(), lanes[B - 1].end(),
[&](int a, int b){ return sor[a].x < sor[b].x; });
add_u(sor[la], sor[lb]);
}
// ---- undirected -> directed snake ----
unordered_map<uint64_t,int> id_of;
vector<P> id_to_pt;
auto get_id = [&](const P& p){
uint64_t k = key64(p);
auto it = id_of.find(k);
if (it != id_of.end()) return it->second;
int nid = (int)id_to_pt.size();
id_of.emplace(k, nid); id_to_pt.push_back(p);
return nid;
};
for (auto &e : undirected) { get_id(e.first); get_id(e.second); }
vector<vector<int>> adj(id_to_pt.size());
for (auto &e : undirected) {
int u = get_id(e.first), v = get_id(e.second);
adj[u].push_back(v); adj[v].push_back(u);
}
auto extract_cycle = [&](int s)->vector<int>{
vector<int> ord;
if (s < 0 || s >= (int)adj.size()) return ord;
if (adj[s].empty()) return ord;
ord.push_back(s);
int prev = -1, cur = s; set<pair<int,int>> used;
for (size_t it = 0; it < adj.size() * 4; ++it) {
int nxt = -1;
for (int v : adj[cur]) {
if (v == prev) continue;
if (used.count({cur, v}) || used.count({v, cur})) continue;
nxt = v; used.insert({cur, v}); break;
}
if (nxt == -1) break;
ord.push_back(nxt);
prev = cur; cur = nxt;
if (cur == s) break;
}
return ord;
};
int start_node = id_of.count(key64(p0)) ? id_of[key64(p0)] : -1;
vector<int> ring = (start_node != -1) ? extract_cycle(start_node) : vector<int>{};
if ((int)ring.size() < 4) {
vector<int> bestcyc;
for (int u = 0; u < (int)adj.size(); ++u) {
auto tmp = extract_cycle(u);
if (tmp.size() > bestcyc.size()) bestcyc = std::move(tmp);
}
ring = std::move(bestcyc);
}
vector<pair<P,P>> directed;
if (!ring.empty()) {
int tdi = 0;
for (int i = 1; i < N; i++)
if (dev[i].x + dev[i].y > dev[tdi].x + dev[tdi].y) tdi = i;
P target = dev[tdi];
auto d2 = [&](const P& a, const P& b)->long long{
long long dx=a.x-b.x, dy=a.y-b.y; return dx*dx+dy*dy;
};
int n = (int)ring.size();
vector<P> rpts(n);
for (int i = 0; i < n; i++) rpts[i] = id_to_pt[ring[i]];
vector<int> cand;
for (int i = 0; i < n; i++) if (band_y(rpts[i].y) % 2 == 1) cand.push_back(i);
if (cand.empty()) for (int i = 0; i < n; i++) cand.push_back(i);
int pivot = cand[0];
for (int i : cand) if (d2(rpts[i], target) < d2(rpts[pivot], target)) pivot = i;
auto cyc_dist = [&](int i, int j){ int a=(j-i+n)%n, b=(i-j+n)%n; return min(a,b); };
vector<int> dval(n);
for (int i = 0; i < n; i++) dval[i] = cyc_dist(i, pivot);
for (int i = 0; i < n; i++) {
int j = (i + 1) % n; P u = rpts[i], v = rpts[j];
if (dval[i] > dval[j]) directed.push_back({u, v});
else if (dval[i] < dval[j]) directed.push_back({v, u});
else directed.push_back((u.x > v.x) ? make_pair(u, v) : make_pair(v, u));
}
directed.push_back({rpts[pivot], target});
}
// ---- base segments(交差検証用)----
vector<pair<P,P>> all_segments;
all_segments.push_back({START, sor[p0_idx]});
for (auto &e : directed) all_segments.push_back(e);
// ---- device wiring(近傍+交差回避、各ソーター最大 outdeg=2 を尊重)----
vector<int> outdeg(M, 0);
unordered_map<uint64_t,int> idxS, idxD;
idxS.reserve(M * 2); idxD.reserve(N * 2);
for (int i = 0; i < M; i++) idxS.emplace(key64(sor[i]), i);
for (int i = 0; i < N; i++) idxD.emplace(key64(dev[i]), i);
for (auto &e : directed) {
auto it = idxS.find(key64(e.first));
if (it != idxS.end()) outdeg[it->second]++;
}
struct Cand { int si; P sp; };
vector<Cand> picker;
for (int i = 0; i < M; i++) if (valid[i]) picker.push_back({i, sor[i]});
vector<pair<int,int>> dev_edges; // (sorter, device)
vector<char> usedS(M, 0);
auto dist2 = [&](const P& a, const P& b)->long long{
long long dx=a.x-b.x, dy=a.y-b.y; return dx*dx+dy*dy;
};
for (int di = 0; di < N; ++di) {
P dp = dev[di];
vector<int> ord(picker.size());
iota(ord.begin(), ord.end(), 0);
sort(ord.begin(), ord.end(),
[&](int a, int b){ return dist2(dp, picker[a].sp) < dist2(dp, picker[b].sp); });
for (int oi : ord) {
int si = picker[oi].si;
if (usedS[si] || outdeg[si] >= 2) continue;
P sp = picker[oi].sp;
bool bad = false;
for (auto &e : all_segments) {
if (segments_intersect(dp, sp, e.first, e.second, true)) { bad = true; break; }
}
if (bad) continue;
for (auto &pr : dev_edges) {
P sp2 = sor[pr.first], dp2 = dev[pr.second];
if (segments_intersect(dp, sp, dp2, sp2, true)) { bad = true; break; }
}
if (bad) continue;
dev_edges.push_back({si, di});
usedS[si] = 1;
outdeg[si]++;
all_segments.push_back({sp, dp});
break;
}
}
// ---- sorter graph(ソーター間の向き)----
vector<vector<int>> G(M);
auto push_from_segments = [&](const vector<pair<P,P>>& segs){
for (const auto& e : segs) {
auto itU = idxS.find(key64(e.first));
auto itV = idxS.find(key64(e.second));
if (itU != idxS.end() && itV != idxS.end()) {
G[itU->second].push_back(itV->second);
}
}
};
push_from_segments(directed);
// ---- 層間のペア枝を追加(主題)----
vector<pair<int,int>> pair_edges;
add_adjacent_lane_pairs_(lanes, G, outdeg, pair_edges, all_segments);
// add_snake_same_lane_and_bridges(lanes, sor, G, outdeg, all_segments);
// ---- Compose outputs ----
auto node_id = [&](const P& p)->int{
auto itD2 = idxD.find(key64(p)); if (itD2 != idxD.end()) return itD2->second;
auto itS2 = idxS.find(key64(p)); if (itS2 != idxS.end()) return N + itS2->second;
return -1;
};
vector<vector<int>> outs(M);
vector<char> installed(M, 0);
for (auto &e : directed) {
auto it = idxS.find(key64(e.first));
if (it != idxS.end()) {
int u = it->second, v = node_id(e.second);
if (v != -1) {
outs[u].push_back(v); installed[u] = 1;
if (v >= N) installed[v - N] = 1;
}
}
}
for (auto &pr : dev_edges) {
int si = pr.first, di = pr.second;
outs[si].push_back(di); installed[si] = 1;
}
for (auto &pr : pair_edges) {
int u = pr.first, v = pr.second;
outs[u].push_back(N + v);
installed[u] = 1; installed[v] = 1;
}
out.s = N + p0_idx;
out.lines.assign(M, "-1");
for (int i = 0; i < M; i++) {
if (!installed[i]) continue;
int a, b;
if (outs[i].empty()) { a = b = 0; }
else if ((int)outs[i].size() == 1) { a = b = outs[i][0]; }
else { a = outs[i][0]; b = outs[i][1]; }
out.lines[i] = "0 " + to_string(a) + " " + to_string(b);
}
out.segments.push_back({START, sor[p0_idx]});
auto point_of_id = [&](int id)->P{
if (id < N) return dev[id];
else return sor[id - N];
};
for (int i = 0; i < M; i++) {
if (out.lines[i] == "-1") continue;
int k, a, b; { stringstream ss(out.lines[i]); ss >> k >> a >> b; }
P spt = sor[i];
out.segments.push_back({spt, point_of_id(a)});
out.segments.push_back({spt, point_of_id(b)});
}
out.ok = true;
return out;
}
};
// ================= Validate crossings =================
static bool has_crossing(const vector<pair<P,P>>& segs){
int E=(int)segs.size();
for(int i=0;i<E;i++){
for(int j=i+1;j<E;j++){
const auto &a=segs[i], &b=segs[j];
auto same = [](const P& u,const P& v){ return u.x==v.x && u.y==v.y; };
if( same(a.first,b.first) || same(a.first,b.second) ||
same(a.second,b.first) || same(a.second,b.second) ) continue;
if(segments_intersect(a.first,a.second,b.first,b.second,true)) return true;
}
}
return false;
}
// ============ Hungarian (maximization, square) ============
static vector<int> hungarian_max_assign(const vector<vector<double>>& w){ // row=color, col=device
int n = (int)w.size();
const double INF = 1e100;
vector<double> u(n+1,0), v(n+1,0);
vector<int> p(n+1,0), way(n+1,0);
for(int i=1;i<=n;i++){
p[0]=i;
int j0=0;
vector<double> minv(n+1, INF);
vector<char> used(n+1,false);
do{
used[j0]=true;
int i0=p[j0], j1=0;
double delta=INF;
for(int j=1;j<=n;j++) if(!used[j]){
double cur = -(w[i0-1][j-1]) - u[i0] - v[j]; // maximize -> negate
if(cur < minv[j]){ minv[j]=cur; way[j]=j0; }
if(minv[j] < delta){ delta=minv[j]; j1=j; }
}
for(int j=0;j<=n;j++){
if(used[j]){ u[p[j]] += delta; v[j] -= delta; }
else minv[j] -= delta;
}
j0=j1;
}while(p[j0]!=0);
do{
int j1=way[j0];
p[j0]=p[j1]; j0=j1;
}while(j0);
}
vector<int> assign_row_to_col(n,-1); // row i -> col j
for(int j=1;j<=n;j++) if(p[j]) assign_row_to_col[p[j]-1]=j-1;
return assign_row_to_col;
}
// ============ reachability & topo (graph fixed) ============
// best.lines[i] : "-1" か "k a b"(a,b は [0..N+M-1])
static void build_reach_and_topo(
const Plan& best, int N, int M,
const vector<string>& lines,
vector<char>& installed, vector<int>& v1, vector<int>& v2,
vector<char>& reachS, vector<int>& topo, vector<int>& rtopo
){
installed.assign(M,0); v1.assign(M,-1); v2.assign(M,-1);
for(int i=0;i<M;i++){
if(lines[i]=="-1") continue;
installed[i]=1;
int k,a,b; stringstream ss(lines[i]); ss>>k>>a>>b;
v1[i]=a; v2[i]=b;
}
vector<vector<int>> adjS(M), parS(M);
auto push_edge = [&](int u,int to){
if(to>=N){
int v=to-N;
if(0<=v&&v<M){ adjS[u].push_back(v); parS[v].push_back(u); }
}
};
for(int u=0;u<M;u++) if(installed[u]){
if(v1[u]!=-1) push_edge(u,v1[u]);
if(v2[u]!=-1) push_edge(u,v2[u]);
}
reachS.assign(M,0);
if(best.s>=N){
int s0=best.s-N;
if(0<=s0&&s0<M&&installed[s0]){
queue<int> q; reachS[s0]=1; q.push(s0);
while(!q.empty()){
int u=q.front(); q.pop();
for(int v: adjS[u]){
if(!installed[v]||reachS[v]) continue;
reachS[v]=1; q.push(v);
}
}
}
}
vector<int> indeg(M,0);
for(int v=0;v<M;v++) if(installed[v]&&reachS[v]){
int c=0; for(int p:parS[v]) if(installed[p]&&reachS[p]) c++; indeg[v]=c;
}
topo.clear(); topo.reserve(M);
{
queue<int> q;
for(int i=0;i<M;i++) if(installed[i]&&reachS[i]&&indeg[i]==0) q.push(i);
while(!q.empty()){
int u=q.front(); q.pop(); topo.push_back(u);
for(int v:adjS[u]){
if(!installed[v]||!reachS[v]) continue;
if(--indeg[v]==0) q.push(v);
}
}
}
rtopo=topo; reverse(rtopo.begin(), rtopo.end());
}
// ============ forward flow (given k & swap) ============
struct FlowPack{
vector<vector<double>> inflS; // [M][N] sorter inflow by color
vector<vector<double>> flowD; // [N][N] device intake by color
};
static FlowPack forward_flow_allcolors(
const Plan& best, int N, int M, int K,
const vector<char>& installed, const vector<int>& v1, const vector<int>& v2,
const vector<int>& curK, const vector<char>& swapFlag,
const vector<char>& reachS, const vector<int>& topo,
const vector<vector<double>>& prob
){
vector<vector<double>> inflS(M, vector<double>(N,0.0));
vector<vector<double>> flowD(N, vector<double>(N,0.0));
if(best.s < N){
int d=best.s;
for(int c=0;c<N;c++) flowD[d][c]+=1.0/N;
}else{
int s0=best.s-N;
if(0<=s0&&s0<M&&installed[s0]&&reachS[s0]){
for(int c=0;c<N;c++) inflS[s0][c]+=1.0/N;
}
}
for(int u: topo){
if(!installed[u]) continue;
double sm=0; for(int c=0;c<N;c++) sm+=inflS[u][c];
if(sm==0) continue;
int a=v1[u], b=v2[u];
if(swapFlag[u]) std::swap(a,b);
int k=curK[u];
auto push=[&](int id,int c,double mass){
if(mass==0) return;
if(id<N) flowD[id][c]+=mass;
else{
int v=id-N; if(0<=v&&v<M) inflS[v][c]+=mass;
}
};
for(int c=0;c<N;c++){
double in=inflS[u][c]; if(in==0) continue;
double p=prob[k][c];
push(a,c,in*p);
push(b,c,in*(1.0-p));
}
}
return {inflS, flowD};
}
// ============ 割当固定で (k, swap) を逆トポ順に更新 ============
static void backward_pick_for_assignment(
int N, int M, int K,
const vector<char>& installed, const vector<int>& v1, const vector<int>& v2,
const vector<vector<double>>& inflS, // forward の各ソーター入口の色分布
const vector<char>& reachS, const vector<int>& rtopo,
const vector<int>& device_of_color, // サイズ N: color c -> device d*
vector<int>& curK, vector<char>& swapFlag,
const vector<vector<double>>& prob
){
auto Rdev = [&](int d,int c)->double{ return (device_of_color[c]==d)?1.0:0.0; };
// R[u][c]: sorter u に入った色 c が「割当デバイスへ到達する」確率
vector<vector<double>> R(M, vector<double>(N,0.0));
auto childR = [&](int id,int c)->double{
if(id<N) return Rdev(id,c);
int v=id-N; if(0<=v&&v<M) return R[v][c];
return 0.0;
};
for(int u: rtopo){
if(!installed[u] || !reachS[u]) continue;
int a0=v1[u], b0=v2[u];
double bestVal=-1e100; int argK=curK[u]; char argS=swapFlag[u];
// 子のRをキャッシュ
vector<double> Ra(N), Rb(N);
for(int c=0;c<N;c++){ Ra[c]=childR(a0,c); Rb[c]=childR(b0,c); }
for(int k=0;k<K;k++){
double vKeep=0.0, vSwap=0.0;
for(int c=0;c<N;c++){
double in = inflS[u][c];
if(in==0) continue;
double p = prob[k][c];
vKeep += in*( p*Ra[c] + (1.0-p)*Rb[c] );
vSwap += in*( p*Rb[c] + (1.0-p)*Ra[c] );
}
if(vKeep>bestVal){ bestVal=vKeep; argK=k; argS=0; }
if(vSwap>bestVal){ bestVal=vSwap; argK=k; argS=1; }
}
curK[u]=argK; swapFlag[u]=argS;
// 選んだ (k,swap) で R[u][c] を確定
int aa=a0, bb=b0; if(argS) std::swap(aa,bb);
for(int c=0;c<N;c++){
double p = prob[argK][c];
R[u][c] = p*childR(aa,c) + (1.0-p)*childR(bb,c);
}
}
}
// best を上書き最適化:分類器番号・出口入替・デバイス割当
// 依存: hungarian_max_assign / build_reach_and_topo / forward_flow_allcolors / backward_pick_for_assignment
// best を上書き最適化:分類器番号・出口入替・デバイス割当(改良版)
// best を上書き最適化:分類器番号・出口入替・デバイス割当(+余剰時間での軽量ヒルクライム)
static void optimize_plan(
Plan& best, int N, int M, int K,
const vector<vector<double>>& prob,
int iters = 10,
int restarts = 10,
int extra_hill_ms = 450, // ★余剰時間で回すヒルクライムの時間上限(ms)
int hill_L = 30, // ★1ラウンドで試すノード数(margin 小い順)
int hill_rounds = 100 // ★ラウンド数
){
using namespace std::chrono;
// ==== グラフ解析(固定配線) ====
vector<char> installed; vector<int> v1, v2;
vector<char> reachS; vector<int> topo, rtopo;
build_reach_and_topo(best, N, M, best.lines,
installed, v1, v2, reachS, topo, rtopo);
// 便利ラムダ
auto sumv = [&](const vector<double>& a){ double s=0; for(double x:a)s+=x; return s; };
auto gini = [&](const vector<double>& a){
double S=sumv(a); if(S<=0) return 0.0; double s2=0; for(double x:a){ double p=x/S; s2+=p*p; }
return 1.0 - s2;
};
auto inc_mix = [&](const vector<double>& before, const vector<double>& add){
double Sb=0,Mb=0; for(double x:before){ Sb+=x; Mb=max(Mb,x); }
double Sa=Sb, Ma=Mb;
for(size_t i=0;i<add.size();++i){ double v=before[i]+add[i]; Sa+=add[i]; Ma=max(Ma,v); }
return (Sa-Ma) - (Sb-Mb);
};
// ========== トップダウン初期化 ==========
auto seed_topdown_once = [&](double w_branch, double w_dev, double w_sorter){
vector<vector<double>> histS(M, vector<double>(N,0.0));
vector<vector<double>> histD(N, vector<double>(N,0.0));
vector<int> initK(M,0);
vector<char> initSwap(M,0);
auto child_hist = [&](int id)->vector<double>&{
return (id<N)? histD[id] : histS[id-N];
};
auto child_w = [&](int id){ return (id<N)? w_dev : w_sorter; };
if(best.s >= N){
int s0=best.s-N;
if(0<=s0 && s0<M && installed[s0] && reachS[s0]){
for(int c=0;c<N;c++) histS[s0][c]+=1.0/N;
}
}else{
int d0=best.s;
for(int c=0;c<N;c++) histD[d0][c]+=1.0/N;
}
for(int u: topo){
if(!(u>=0 && u<M)) continue;
if(!installed[u] || !reachS[u]) continue;
auto &fin = histS[u];
if(sumv(fin)==0) continue;
int a=v1[u], b=v2[u]; if(a==-1||b==-1) continue;
auto &HA=child_hist(a); auto &HB=child_hist(b);
double bestCost=1e100; int argK=0; char argS=0;
vector<double> fA(N), fB(N);
for(int k=0;k<K;k++){
for(int c=0;c<N;c++){ double p=prob[k][c], x=fin[c]; fA[c]=x*p; fB[c]=x*(1.0-p); }
double SA=sumv(fA), SB=sumv(fB);
double wA=(SA+SB>0)? SA/(SA+SB):0.0, wB=1.0-wA;
double branch = w_branch*( wA*gini(fA) + wB*gini(fB) );
double keep = branch + child_w(a)*inc_mix(HA,fA) + child_w(b)*inc_mix(HB,fB);
double swap = branch + child_w(a)*inc_mix(HA,fB) + child_w(b)*inc_mix(HB,fA);
if(keep<bestCost){ bestCost=keep; argK=k; argS=0; }
if(swap<bestCost){ bestCost=swap; argK=k; argS=1; }
}
initK[u]=argK; initSwap[u]=argS;
// 反映
for(int c=0;c<N;c++){
double p=prob[argK][c], x=fin[c];
double L=x*p, R=x*(1.0-p);
if(!argS){ child_hist(a)[c]+=L; child_hist(b)[c]+=R; }
else { child_hist(a)[c]+=R; child_hist(b)[c]+=L; }
}
}
return pair<vector<int>,vector<char>>(std::move(initK), std::move(initSwap));
};
// ========== backward:最良+次善手と margin を返す ==========
auto backward_best_and_second = [&]( // curK/swap を最適にし、2番手とマージンも返す
const vector<vector<double>>& inflS,
const vector<int>& color2dev,
vector<int>& curK, vector<char>& swapFlag,
vector<int>& secK, vector<char>& secSwap, vector<double>& margin
){
auto Rdev = [&](int d,int c)->double{ return (color2dev[c]==d)?1.0:0.0; };
vector<vector<double>> R(M, vector<double>(N,0.0));
auto childR = [&](int id,int c)->double{
if(id<N) return Rdev(id,c);
int v=id-N; if(0<=v&&v<M) return R[v][c];
return 0.0;
};
secK.assign(M,0); secSwap.assign(M,0); margin.assign(M,0.0);
for(int u: rtopo){
if(!installed[u] || !reachS[u]) continue;
int a0=v1[u], b0=v2[u];
vector<double> Ra(N), Rb(N);
for(int c=0;c<N;c++){ Ra[c]=childR(a0,c); Rb[c]=childR(b0,c); }
double bestv=-1e100, second=-1e100; int argK=curK[u], argS=swapFlag[u];
int argK2=0; char argS2=0;
auto eval = [&](int k, int sw)->double{
double val=0.0;
for(int c=0;c<N;c++){
double in=inflS[u][c]; if(in==0) continue;
double p=prob[k][c];
if(!sw) val += in*( p*Ra[c] + (1.0-p)*Rb[c] );
else val += in*( p*Rb[c] + (1.0-p)*Ra[c] );
}
return val;
};
for(int k=0;k<K;k++){
double v0=eval(k,0);
if(v0>bestv){ second=bestv; argK2=argK; argS2=argS; bestv=v0; argK=k; argS=0; }
else if(v0>second){ second=v0; argK2=k; argS2=0; }
double v_sw=eval(k,1); // ← rename: v1 -> v_sw(shadow 回避)
if(v_sw>bestv){ second=bestv; argK2=argK; argS2=argS; bestv=v_sw; argK=k; argS=1; }
else if(v_sw>second){ second=v_sw; argK2=k; argS2=1; }
}
curK[u]=argK; swapFlag[u]=argS;
secK[u]=argK2; secSwap[u]=argS2; margin[u]=bestv-second;
// R[u] を確定(best)
int aa=a0, bb=b0; if(argS) std::swap(aa,bb);
for(int c=0;c<N;c++){
double p=prob[argK][c];
R[u][c] = p*childR(aa,c) + (1.0-p)*childR(bb,c);
}
}
};
// ========== 評価 ==========
auto score_from_flow = [&](const vector<vector<double>>& flowD,
const vector<int>& color2dev)->double{
double s=0.0;
for(int c=0;c<N;c++){ int d=color2dev[c]; if(0<=d && d<N) s+=flowD[d][c]; }
return s;
};
auto eval_with_hungarian = [&](const vector<int>& curK, const vector<char>& swapFlag,
vector<int>* out_assign=nullptr, vector<vector<double>>* out_flowD=nullptr)->double{
auto fp = forward_flow_allcolors(best, N, M, K, installed, v1, v2, curK, swapFlag, reachS, topo, prob);
vector<vector<double>> W(N, vector<double>(N,0.0));
for(int d=0; d<N; ++d) for(int c=0; c<N; ++c) W[c][d]=fp.flowD[d][c];
auto col2dev = hungarian_max_assign(W);
if(out_assign) *out_assign = col2dev;
if(out_flowD) *out_flowD = fp.flowD;
return score_from_flow(fp.flowD, col2dev);
};
// ベスト解の保持
double bestScore=-1.0; vector<int> best_d(N,-1);
vector<int> bestK; vector<char> bestSwap;
std::mt19937 rng(1234567);
// ==== シード集合 ====
vector<pair<vector<int>,vector<char>>> seeds;
{ // 既存 lines
vector<int> k0(M,0); vector<char> s0(M,0);
for(int i=0;i<M;i++) if(installed[i]){
int k,a,b; stringstream ss(best.lines[i]); ss>>k>>a>>b;
k0[i]=max(0,min(K-1,k));
}
seeds.push_back({std::move(k0), std::move(s0)});
}
seeds.push_back( seed_topdown_once(0.35, 1.00, 0.25) );
seeds.push_back( seed_topdown_once(0.35, 1.30, 0.35) );
while((int)seeds.size()<restarts){
vector<int> k(M,0); vector<char> s(M,0);
for(int i=0;i<M;i++) if(installed[i]){
if(K>1 && (rng()%3==0)) k[i]=rng()%K;
if(rng()%5==0) s[i]=1;
}
seeds.push_back({std::move(k), std::move(s)});
}
// ==== 各シードで探索 ====
for(auto seed : seeds){
vector<int> curK = std::move(seed.first);
vector<char> swapFlag = std::move(seed.second);
// 小さな初期摂動
for(int i=0;i<M;i++) if(installed[i] && rng()%12==0){
if(K>1) curK[i]=(curK[i]+1+(rng()%2))%K;
if(rng()%2) swapFlag[i]^=1;
}
vector<int> color2dev(N,0);
for(int it=0; it<iters; ++it){
// forward
auto fp = forward_flow_allcolors(best, N, M, K,
installed, v1, v2,
curK, swapFlag,
reachS, topo, prob);
// Hungarian(色→デバイス)
vector<vector<double>> W(N, vector<double>(N,0.0));
for(int d=0; d<N; ++d) for(int c=0; c<N; ++c) W[c][d]=fp.flowD[d][c];
auto col2dev = hungarian_max_assign(W);
color2dev = col2dev;
// backward:最良+次善手と margin
vector<int> secK; vector<char> secSwap; vector<double> margin;
backward_best_and_second(fp.inflS, color2dev,
curK, swapFlag,
secK, secSwap, margin);
// 少数ノードに摂動(マージン小を優先)
vector<int> idx; idx.reserve(M);
for(int i=0;i<M;i++) if(installed[i] && reachS[i]) idx.push_back(i);
int perturb = max(1, (int)(idx.size() * (0.05 + 0.10*(iters-1-it)/max(1,iters-1)))); // 5〜15%
perturb = min(perturb, (int)idx.size());
nth_element(idx.begin(), idx.begin()+perturb, idx.end(),
[&](int a,int b){ return margin[a] < margin[b]; });
for(int t=0;t<perturb; ++t){
int u = idx[t];
curK[u] = secK[u];
if( (bool)swapFlag[u] != (bool)secSwap[u] ) swapFlag[u] ^= 1;
}
}
// 仕上げ forward & Hungarian でスコア
vector<vector<double>> flowD;
auto fp_score = eval_with_hungarian(curK, swapFlag, nullptr, &flowD);
if(fp_score > bestScore){
bestScore = fp_score;
// 割当を色→デバイスからデバイス→色へ
vector<vector<double>> W(N, vector<double>(N,0.0));
for(int d=0; d<N; ++d) for(int c=0; c<N; ++c) W[c][d]=flowD[d][c];
auto col2dev = hungarian_max_assign(W);
best_d.assign(N,-1);
for(int c=0;c<N;c++){ int d=col2dev[c]; if(0<=d && d<N) best_d[d]=c; }
bestK = curK; bestSwap = swapFlag;
}
}
// ==== ★ 余剰時間ヒルクライム(sec-best ピボット)====
{
auto t0 = steady_clock::now();
vector<int> curK = bestK; vector<char> swapFlag = bestSwap;
// 現在の割当・流量
vector<int> color2dev;
vector<vector<double>> flowD;
double curScore = eval_with_hungarian(curK, swapFlag, &color2dev, &flowD);
// inflS を得るため forward を一度(割当は使わない)
auto fp0 = forward_flow_allcolors(best, N, M, K, installed, v1, v2, curK, swapFlag, reachS, topo, prob);
for(int rd=0; rd<hill_rounds; ++rd){
// backward で “2番手” と margin を用意
vector<int> secK; vector<char> secSwap; vector<double> margin;
backward_best_and_second(fp0.inflS, color2dev, curK, swapFlag, secK, secSwap, margin);
// 候補ノード(margin 小さい順 上位 L)
vector<int> cand; cand.reserve(M);
for(int u=0;u<M;u++) if(installed[u] && reachS[u]) cand.push_back(u);
int L = min(hill_L, (int)cand.size());
nth_element(cand.begin(), cand.begin()+L, cand.end(),
[&](int a,int b){ return margin[a] < margin[b]; });
cand.resize(L);
bool improved = false;
for(int u: cand){
if(duration_cast<milliseconds>(steady_clock::now()-t0).count() > extra_hill_ms) break;
// ピボットして評価
int oldK = curK[u]; char oldS = swapFlag[u];
curK[u] = secK[u];
if( (bool)swapFlag[u] != (bool)secSwap[u] ) swapFlag[u] ^= 1;
double sc = eval_with_hungarian(curK, swapFlag);
if(sc > curScore + 1e-12){
curScore = sc; improved = true;
// forward も更新(次ラウンドの inflS 用)
fp0 = forward_flow_allcolors(best, N, M, K, installed, v1, v2, curK, swapFlag, reachS, topo, prob);
// color2dev も更新
vector<vector<double>> W(N, vector<double>(N,0.0));
for(int d=0; d<N; ++d) for(int c=0; c<N; ++c) W[c][d]=fp0.flowD[d][c];
color2dev = hungarian_max_assign(W);
}else{
// 戻す
curK[u] = oldK; swapFlag[u] = oldS;
}
}
if(!improved) break;
}
if(curScore > bestScore + 1e-12){
bestScore = curScore;
// 最終の割当を更新
auto fp = forward_flow_allcolors(best, N, M, K, installed, v1, v2, curK, swapFlag, reachS, topo, prob);
vector<vector<double>> W(N, vector<double>(N,0.0));
for(int d=0; d<N; ++d) for(int c=0; c<N; ++c) W[c][d]=fp.flowD[d][c];
auto col2dev = hungarian_max_assign(W);
best_d.assign(N,-1);
for(int c=0;c<N;c++){ int d=col2dev[c]; if(0<=d && d<N) best_d[d]=c; }
bestK = std::move(curK); bestSwap = std::move(swapFlag);
}
}
// ==== Plan に書き戻し ====
if(bestScore >= 0.0){
best.d = best_d;
for(int i=0;i<M;i++){
if(best.lines[i]=="-1") continue;
int dummy,a,b; { stringstream ss(best.lines[i]); ss>>dummy>>a>>b; }
int k = (i<(int)bestK.size()? bestK[i] : 0);
if(i<(int)bestSwap.size() && bestSwap[i]) std::swap(a,b);
best.lines[i] = to_string(k) + " " + to_string(a) + " " + to_string(b);
}
}
}
// ===================== main =====================
// 既存の P, Plan, Builder, has_crossing を利用
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N,M,K;
if(!(cin>>N>>M>>K)) return 0;
vector<P> dev(N), sor(M);
for(int i=0;i<N;i++) cin>>dev[i].x>>dev[i].y;
for(int i=0;i<M;i++) cin>>sor[i].x>>sor[i].y;
vector<vector<double>> prob(K, vector<double>(N,0.5));
for(int k=0;k<K;k++) for(int c=0;c<N;c++){ double x; cin>>x; prob[k][c]=x; }
// 既存ビルダ(グラフは変えない)
vector<int> Bs = {16,14,12,10,8,6,4,2};
Plan best; bool found=false;
int chosen_B = -1;
for(int B: Bs){
Builder bd(N,M,K,B,dev,sor);
Plan p = bd.build();
if(!has_crossing(p.segments)){ best=std::move(p); found=true; break; }
}
if(!found){
for(int i=0;i<N;i++){ if(i) cout<<' '; cout<<i; } cout<<"\n";
cout<<0<<"\n";
for(int i=0;i<M;i++) cout<<-1<<"\n";
return 0;
}
// ★ 交互最適化(Forward→Hungarian→Backward)
// reorient_edges_bfs_snake_downhill(best, N, M, chosen_B, sor);
// reorient_edges_by_lane_x(best, N, M, chosen_B, sor);
optimize_plan(best, N, M, K, prob);
// 出力
for(int i=0;i<N;i++){ if(i) cout<<' '; cout<<best.d[i]; } cout<<"\n";
cout<<best.s<<"\n";
for(int i=0;i<M;i++) cout<<best.lines[i]<<"\n";
return 0;
}
提出情報
コンパイルエラー
Main.cpp: In function ‘FlowPack forward_flow_allcolors(const Plan&, int, int, int, const std::vector<char>&, const std::vector<int>&, const std::vector<int>&, const std::vector<int>&, const std::vector<char>&, const std::vector<char>&, const std::vector<int>&, const std::vector<std::vector<double> >&)’:
Main.cpp:715:41: warning: unused parameter ‘K’ [-Wunused-parameter]
715 | const Plan& best, int N, int M, int K,
| ~~~~^
Main.cpp: In function ‘int main()’:
Main.cpp:1165:9: warning: unused variable ‘chosen_B’ [-Wunused-variable]
1165 | int chosen_B = -1;
| ^~~~~~~~
Main.cpp: At global scope:
Main.cpp:760:13: warning: ‘void backward_pick_for_assignment(int, int, int, const std::vector<char>&, const std::vector<int>&, const std::vector<int>&, const std::vector<std::vector<double> >&, const std::vector<char>&, const std::vector<int>&, const std::vector<int>&, std::vector<int>&, std::vector<char>&, const std::vector<std::vector<double> >&)’ defined but not used [-Wunused-function]
760 | static void backward_pick_for_assignment(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
Main.cpp:101:13: warning: ‘void add_snake_same_lane_and_bridges(const std::vector<std::vector<int> >&, const std::vector<P>&, std::vector<std::vector<int> >&, std::vector<int>&, std::vector<std::pair<P, P> >&)’ defined but not used [-Wunused-function]
101 | static void add_snake_same_lane_and_bridges(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ジャッジ結果
| セット名 |
test_ALL |
| 得点 / 配点 |
33845281988 / 50000000000 |
| 結果 |
|
| セット名 |
テストケース |
| test_ALL |
test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt |
| ケース名 |
結果 |
実行時間 |
メモリ |
| test_0000.txt |
AC |
6 ms |
3680 KiB |
| test_0001.txt |
AC |
86 ms |
4064 KiB |
| test_0002.txt |
AC |
96 ms |
4084 KiB |
| test_0003.txt |
AC |
5 ms |
3908 KiB |
| test_0004.txt |
AC |
282 ms |
4324 KiB |
| test_0005.txt |
AC |
39 ms |
3984 KiB |
| test_0006.txt |
AC |
24 ms |
3968 KiB |
| test_0007.txt |
AC |
23 ms |
3960 KiB |
| test_0008.txt |
AC |
60 ms |
3908 KiB |
| test_0009.txt |
AC |
23 ms |
3940 KiB |
| test_0010.txt |
AC |
147 ms |
3936 KiB |
| test_0011.txt |
AC |
101 ms |
4136 KiB |
| test_0012.txt |
AC |
4 ms |
3656 KiB |
| test_0013.txt |
AC |
176 ms |
4140 KiB |
| test_0014.txt |
AC |
214 ms |
4112 KiB |
| test_0015.txt |
AC |
144 ms |
4132 KiB |
| test_0016.txt |
AC |
79 ms |
3984 KiB |
| test_0017.txt |
AC |
41 ms |
4024 KiB |
| test_0018.txt |
AC |
110 ms |
4072 KiB |
| test_0019.txt |
AC |
108 ms |
4260 KiB |
| test_0020.txt |
AC |
38 ms |
3972 KiB |
| test_0021.txt |
AC |
40 ms |
3944 KiB |
| test_0022.txt |
AC |
52 ms |
4012 KiB |
| test_0023.txt |
AC |
114 ms |
4148 KiB |
| test_0024.txt |
AC |
8 ms |
3980 KiB |
| test_0025.txt |
AC |
112 ms |
4164 KiB |
| test_0026.txt |
AC |
102 ms |
4080 KiB |
| test_0027.txt |
AC |
94 ms |
4044 KiB |
| test_0028.txt |
AC |
109 ms |
4068 KiB |
| test_0029.txt |
AC |
17 ms |
3980 KiB |
| test_0030.txt |
AC |
60 ms |
3956 KiB |
| test_0031.txt |
AC |
18 ms |
3988 KiB |
| test_0032.txt |
AC |
7 ms |
3856 KiB |
| test_0033.txt |
AC |
9 ms |
3880 KiB |
| test_0034.txt |
AC |
23 ms |
3996 KiB |
| test_0035.txt |
AC |
13 ms |
3916 KiB |
| test_0036.txt |
AC |
221 ms |
4348 KiB |
| test_0037.txt |
AC |
80 ms |
4196 KiB |
| test_0038.txt |
AC |
54 ms |
3968 KiB |
| test_0039.txt |
AC |
24 ms |
3980 KiB |
| test_0040.txt |
AC |
16 ms |
3848 KiB |
| test_0041.txt |
AC |
172 ms |
4212 KiB |
| test_0042.txt |
AC |
45 ms |
3924 KiB |
| test_0043.txt |
AC |
32 ms |
3960 KiB |
| test_0044.txt |
AC |
194 ms |
4316 KiB |
| test_0045.txt |
AC |
20 ms |
3736 KiB |
| test_0046.txt |
AC |
21 ms |
3904 KiB |
| test_0047.txt |
AC |
5 ms |
3664 KiB |
| test_0048.txt |
AC |
42 ms |
3872 KiB |
| test_0049.txt |
AC |
185 ms |
4392 KiB |