Codeforces Round #551 (Div. 2) E. Serval and Snake
解法
- パスの端点を一つも含まないような範囲
範囲に入った後出ることを繰り返すので返ってくる値は必ず偶数 - パスの端点を両方含むような範囲
範囲から出た後入るを繰り返すので返ってくる値は必ず偶数 - パスの端点を片方だけ含む範囲
範囲内と範囲外を往復したあと出て終わりになるので返ってくる値は必ず奇数
各列/行を覆うような範囲を指定してどの行/列に端点があるのか?を特定する。その後二分探索を行うことで返ってくる値が奇数の範囲を見つける。
行/列の特定に1000回、二分探索にそれぞれ10回最悪でかかるので最悪ケースが来ると条件を満たさない。しかし行/列を指定する順番をランダムにすると最悪を引く確率はかなり小さそうだったので投げると通った。
#include <bits/stdc++.h> using namespace std; using ll = long long; // #define int ll using PII = pair<ll, ll>; #define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i) #define REP(i, n) FOR(i, 0, n) #define ALL(x) x.begin(), x.end() template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); } template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); } template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; } template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); } template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T,typename... Ts> auto make_v(size_t a,Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...)); } template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type fill_v(T &t, const V &v) { t=v; } template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); } template<class S,class T> ostream &operator <<(ostream& out,const pair<S,T>& a) { out<<'('<<a.first<<','<<a.second<<')'; return out; } template<class T> ostream &operator <<(ostream& out,const vector<T>& a){ out<<'['; for(const T &i: a) out<<i<<','; out<<']'; return out; } template<class T> ostream &operator <<(ostream& out, const set<T>& a) { out<<'{'; for(const T &i: a) out<<i<<','; out<<'}'; return out; } template<class T, class S> ostream &operator <<(ostream& out, const map<T,S>& a) { out<<'{'; for(auto &i: a) out<<i<<','; out<<'}'; return out; } int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL const int INF = 1<<30; const ll LLINF = 1LL<<60; const ll MOD = 1000000007; // #define DEBUG ll query(ll x1, ll y1, ll x2, ll y2) { #ifdef DEBUG #else cout << "? " << y1+1 << " " << x1+1 << " " << y2+1 << " " << x2+1 << endl; ll ret; cin >> ret; return ret; #endif } signed main(void) { cin.tie(0); ios::sync_with_stdio(false); ll n; cin >> n; vector<ll> idx(n); iota(ALL(idx), 0); mt19937 mt(chrono::steady_clock::now().time_since_epoch().count()); shuffle(ALL(idx), mt); PII p1({-1, -1}), p2({-1, -1}); for(auto i: idx) { ll num = query(0, i, n-1, i); if(num%2) { if(p1.first == -1 && p1.second == -1) p1.first = -1, p1.second = i; else { p2.first = -1, p2.second = i; break; } } } for(auto i: idx) { if(p2.first != -1 || p2.second != -1) break; ll num = query(i, 0, i, n-1); if(num%2) { if(p1.first == -1 && p1.second == -1) p1.first = i, p1.second = -1; else { p2.first = i, p2.second = -1; break; } } } ll lb=0,ub=n; while(ub-lb>1) { ll mid = (lb+ub)/2; ll num; if(p1.first == -1) num = query(mid, p1.second, ub-1, p1.second); else num = query(p1.first, mid, p1.first, ub-1); if(num%2) lb = mid; else ub = mid; } ll x1, y1; if(p1.first == -1) x1 = lb, y1 = p1.second; else x1 = p1.first, y1 = lb; lb=0,ub=n; while(ub-lb>1) { ll mid = (lb+ub)/2; ll num; if(p2.first == -1) num = query(mid, p2.second, ub-1, p2.second); else num = query(p2.first, mid, p2.first, ub-1); if(num%2) lb = mid; else ub = mid; } ll x2, y2; if(p2.first == -1) x2 = lb, y2 = p2.second; else x2 = p2.first, y2 = lb; cout << "! " << y1+1 << " " << x1+1 << " " << y2+1 << " " << x2+1 << endl; return 0; }