Submission #1274817

#TimeUsernameProblemLanguageResultExecution timeMemory
1274817mehrzadObstacles for a Llama (IOI25_obstacles)C++20
37 / 100
218 ms73540 KiB
#include <bits/stdc++.h> using namespace std; using ll = long long; const ll NEG = -4e18; // sentinel for impossible B /*---------------------------------------------------------------*/ /* segment tree for (max B , index) */ struct SegMaxB { int n; vector<pair<ll,int>> t; // (B , index) SegMaxB(int _n=0){init(_n);} void init(int _n){ n=1; while(n<_n) n<<=1; t.assign(2*n, {NEG,-1}); } // set position p (0‑based) void setVal(int p, ll B, int idx){ p+=n; t[p] = {B, idx}; for(p>>=1; p; p>>=1){ t[p] = (t[p<<1].first >= t[p<<1|1].first) ? t[p<<1] : t[p<<1|1]; } } // query max on [l,r] inclusive pair<ll,int> query(int l,int r){ if(l>r) return {NEG,-1}; l+=n; r+=n; pair<ll,int> ans = {NEG,-1}; while(l<=r){ if(l&1){ if(t[l].first > ans.first) ans = t[l]; ++l; } if(!(r&1)){ if(t[r].first > ans.first) ans = t[r]; --r; } l>>=1; r>>=1; } return ans; } }; /*---------------------------------------------------------------*/ /* segment tree for range maximum of H */ struct SegMaxH { int n; vector<int> t; SegMaxH(int _n=0){init(_n);} void init(int _n){ n=1; while(n<_n) n<<=1; t.assign(2*n, INT_MIN); } void setVal(int p, int val){ p+=n; t[p]=val; for(p>>=1; p; p>>=1) t[p]=max(t[p<<1], t[p<<1|1]); } int query(int l,int r){ // inclusive if(l>r) return INT_MIN; l+=n; r+=n; int ans = INT_MIN; while(l<=r){ if(l&1) ans = max(ans, t[l++]); if(!(r&1)) ans = max(ans, t[r--]); l>>=1; r>>=1; } return ans; } }; /*---------------------------------------------------------------*/ /* sparse table for range minimum/maximum (integer) */ struct SparseTable { int N, K; vector<vector<int>> st; vector<int> lg; bool isMin; // true -> min, false -> max SparseTable(){} SparseTable(const vector<int> &a, bool _isMin){ build(a,_isMin); } void build(const vector<int> &a, bool _isMin){ isMin = _isMin; N = (int)a.size(); K = 1; while((1<<K) <= N) ++K; lg.assign(N+1,0); for(int i=2;i<=N;i++) lg[i]=lg[i/2]+1; st.assign(K, vector<int>(N)); st[0]=a; for(int k=1;k<K;k++){ for(int i=0;i+(1<<k)<=N;i++){ if(isMin) st[k][i]=min(st[k-1][i], st[k-1][i+(1<<(k-1))]); else st[k][i]=max(st[k-1][i], st[k-1][i+(1<<(k-1))]); } } } // query on [l,r] inclusive int query(int l,int r) const{ if(l>r) return isMin?INT_MAX:INT_MIN; int k = lg[r-l+1]; if(isMin) return min(st[k][l], st[k][r-(1<<k)+1]); else return max(st[k][l], st[k][r-(1<<k)+1]); } }; /*---------------------------------------------------------------*/ /* global data */ static vector<int> T, H; static vector<int> prefMin, prefMax; static vector<int> deepest; // -1 … N-1 static vector<ll> B; // -inf for useless columns static vector<int> blockId; // -1 for non‑free0 static vector<int> blockLeft, blockRight; // per block static SegMaxB segB; static SegMaxH segH; static SparseTable stLeftGood, stRightGood; // leftGood min , rightGood max static vector<int> leftGood, rightGood; // size M-1 /*---------------------------------------------------------------*/ void initialize(vector<int> TT, vector<int> HH){ T.swap(TT); H.swap(HH); int N = T.size(); int M = H.size(); /* prefix minima / maxima of T */ prefMin.resize(N); prefMax.resize(N); for(int i=0;i<N;i++){ prefMin[i] = (i==0? T[i] : min(prefMin[i-1], T[i])); prefMax[i] = (i==0? T[i] : max(prefMax[i-1], T[i])); } /* deepest and B for every column */ deepest.assign(M, -1); B.assign(M, NEG); for(int j=0;j<M;j++){ // first index where prefMin <= H[j] int lo=0, hi=N-1, pos=N; while(lo<=hi){ int mid=(lo+hi)/2; if(prefMin[mid] <= H[j]){ pos=mid; hi=mid-1; }else lo=mid+1; } if(pos==0) deepest[j] = -1; else if(pos==N) deepest[j] = N-1; else deepest[j] = pos-1; if(deepest[j] >= 0) B[j] = prefMax[deepest[j]]; } /* free0 columns and block decomposition */ blockId.assign(M, -1); vector<int> blockOfCol; // only for free columns blockLeft.clear(); blockRight.clear(); int curBlk = -1; for(int j=0;j<M;j++){ if(H[j] < T[0]){ if(curBlk==-1){ curBlk = (int)blockLeft.size(); blockLeft.push_back(j); blockRight.push_back(j); blockOfCol.push_back(curBlk); }else{ blockRight[curBlk]=j; blockOfCol.push_back(curBlk); } blockId[j]=curBlk; }else{ curBlk=-1; blockOfCol.push_back(-1); } } /* segment tree for max B (only for free0 columns) */ segB.init(M); for(int j=0;j<M;j++){ if(B[j]!=NEG) segB.setVal(j, B[j], j); } /* segment tree for max H */ segH.init(M); for(int j=0;j<M;j++) segH.setVal(j, H[j]); /* ----- compute leftGood and rightGood ---------------------- */ // first next index with H >= B vector<int> nextGe(M, M); for(int j=0;j<M;j++){ if(B[j]==NEG){ nextGe[j]=j+1; continue; } int lo=j+1, hi=M-1, pos=M; while(lo<=hi){ int mid=(lo+hi)/2; if(H[mid] >= B[j]){ pos=mid; hi=mid-1; }else lo=mid+1; } nextGe[j]=pos; } // rightLimit from (3) vector<int> rightLimit(M, -2); // -2 means cannot cross any border for(int j=0;j<M;j++){ if(B[j]==NEG) continue; rightLimit[j] = nextGe[j]-2; // may become -1, -2 etc. } // sweep left to right, maintain active columns (rightLimit >= cur) leftGood.assign(max(0,M-1), -1); vector<vector<int>> deactivate(M); // at border x we deactivate cols with rightLimit == x-1 for(int j=0;j<M;j++){ if(rightLimit[j]>=j){ int stop = rightLimit[j]+1; // first border where it stops if(stop < M) deactivate[stop].push_back(j); } } // segment tree for max index among active columns struct SegIdx { int n; vector<int> t; SegIdx(int _n=0){init(_n);} void init(int _n){ n=1; while(n<_n) n<<=1; t.assign(2*n, -1); } void setVal(int p, int v){ p+=n; t[p]=v; for(p>>=1;p;p>>=1) t[p]=max(t[p<<1], t[p<<1|1]); } int query(int l,int r){ if(l>r) return -1; l+=n; r+=n; int ans=-1; while(l<=r){ if(l&1) ans=max(ans,t[l++]); if(!(r&1)) ans=max(ans,t[r--]); l>>=1; r>>=1; } return ans; } } segIdx; segIdx.init(M); // initially none active for(int border=0; border<M-1; ++border){ // activate column border (it becomes usable exactly at its own border) if(rightLimit[border]>=border){ segIdx.setVal(border, border); } // deactivate columns that stop before next border for(int col: deactivate[border+1]){ segIdx.setVal(col, -1); } leftGood[border] = segIdx.query(0, border); } // now rightGood (mirror) vector<int> prevGe(M, -1); for(int j=0;j<M;j++){ if(B[j]==NEG){ prevGe[j]=j-1; continue; } int lo=0, hi=j-1, pos=-1; while(lo<=hi){ int mid=(lo+hi)/2; if(H[mid] >= B[j]){ pos=mid; lo=mid+1; }else hi=mid-1; } prevGe[j]=pos; } vector<int> leftLimit(M, -2); for(int j=0;j<M;j++){ if(B[j]==NEG) continue; leftLimit[j] = prevGe[j]+1; // first border that can be crossed from right side } // sweep right to left, maintain active columns (leftLimit <= cur) rightGood.assign(max(0,M-1), M); vector<vector<int>> activate(M); // at border x we activate columns with leftLimit == x for(int j=0;j<M;j++){ if(leftLimit[j]<=j-1){ int start = leftLimit[j]; if(start>=0) activate[start].push_back(j); } } // segment tree for minimal index among active columns struct SegMinIdx { int n; vector<int> t; SegMinIdx(int _n=0){init(_n);} void init(int _n){ n=1; while(n<_n) n<<=1; t.assign(2*n, INT_MAX); } void setVal(int p, int v){ p+=n; t[p]=v; for(p>>=1;p;p>>=1) t[p]=min(t[p<<1], t[p<<1|1]); } int query(int l,int r){ if(l>r) return INT_MAX; l+=n; r+=n; int ans=INT_MAX; while(l<=r){ if(l&1) ans=min(ans,t[l++]); if(!(r&1)) ans=min(ans,t[r--]); l>>=1; r>>=1; } return ans; } } segMinIdx; segMinIdx.init(M); // initially none active for(int border=M-2; border>=0; --border){ // activate columns that become usable at this border for(int col: activate[border]){ segMinIdx.setVal(col, col); } // columns that are to the left of this border are not usable any more, // but we never deactivate because once leftLimit <= border it stays true. int cur = segMinIdx.query(border+1, M-1); if(cur==INT_MAX) cur = M; // meaning not existing rightGood[border] = cur; } // build sparse tables for leftGood (min) and rightGood (max) if(M>=2){ stLeftGood = SparseTable(leftGood, true); // min stRightGood = SparseTable(rightGood, false); // max } } /*---------------------------------------------------------------*/ bool can_reach(int L, int R, int S, int D){ if(S==D) return true; int blkS = blockId[S]; int blkD = blockId[D]; if(blkS==-1 || blkD==-1) return false; // should not happen if(blkS == blkD) return true; // same row‑0 block // intersection of destination block with the interval int dL = max(L, blockLeft[blkD]); int dR = min(R, blockRight[blkD]); if(dL > dR) return false; // destination block not inside interval // intersection of start block with the interval int sL = max(L, blockLeft[blkS]); int sR = min(R, blockRight[blkS]); if(sL > sR) return false; // best (max B) column inside start block part auto bestStart = segB.query(sL, sR); ll BmaxStart = bestStart.first; int p = bestStart.second; // column index of that best start column // best (max B) column inside destination block part auto bestDest = segB.query(dL, dR); ll BmaxDest = bestDest.first; // threshold V ll V = min(BmaxStart, BmaxDest); if(V==NEG) return false; // should not happen // column q of destination block that is closest to p int q; if(S < D) q = dL; // leftmost column of dest block else q = dR; // rightmost column of dest block int lo = min(p,q), hi = max(p,q); int maxH = segH.query(lo, hi); return ( (ll)maxH < V ); }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...