#include using namespace std; using S1 = long long; class SegmentTree1{ public: int siz = -1,n = -1; vector dat; S1 op(S1 a, S1 b){return a+b;} S1 e(){return 0;} void renew (S1 &a,S1 x){ a = op(a,x); //a = x; //set(pos,x)で可能. //その他. } SegmentTree1(int N){init(N);} SegmentTree1(const vector &A){//長さ配列サイズに合わせる. siz = 1; n = A.size(); while(siz < n) siz *= 2; dat.resize(siz*2,e()); for(int i=0; i0; i--) dat.at(i) = op(dat.at(i*2),dat.at(i*2+1)); } void init(int N){ //全要素単位元に初期化. siz = 1; n = N; while(siz < n) siz *= 2; dat.assign(siz*2,e()); } void init(const vector &A){//長さ配列サイズに合わせる. siz = 1; n = A.size(); while(siz < n) siz *= 2; dat.resize(siz*2,e()); for(int i=0; i0; i--) dat.at(i) = op(dat.at(i*2),dat.at(i*2+1)); } void set(int pos,S1 x){ pos = pos+siz; dat.at(pos) = x; while(pos != 1){ pos = pos/2; dat.at(pos) = op(dat.at(pos*2),dat.at(pos*2+1)); } } void update(int pos,S1 x){ pos = pos+siz; renew(dat.at(pos),x); while(pos != 1){ pos = pos/2; dat.at(pos) = op(dat.at(pos*2),dat.at(pos*2+1)); } } S1 findans(int l, int r){ S1 retl = e(),retr = e(); l += siz,r += siz; while(l < r){ if(l&1) retl = op(retl,dat.at(l++)); if(r&1) retr = op(dat.at(--r),retr); l >>= 1; r >>= 1; } return op(retl,retr); } S1 get(int pos){return dat.at(pos+siz);} S1 rangeans(int l, int r){return findans(l,r);} S1 allrange(){return dat.at(1);} //rightは) leftは[で 渡す&返す. int maxright(const function f,int l = 0){ //fを満たさない最小の箇所を返す なければn. l += siz; int r = n+siz; vector ls,rs; while(l < r){ if(l&1) ls.push_back(l++); if(r&1) rs.push_back(--r); l >>= 1; r >>= 1; } S1 okl = e(); for(int i=0; i=0; i--){ l = rs.at(i); S1 now = op(okl,dat.at(l)); if(!f(now)){ while(l < siz){ l <<= 1; now = op(okl,dat.at(l)); if(f(now)){okl = now; l++;} } return l-siz; } okl = now; } return n; } int minleft(const function f,int r = -1){ //fを満たす最小の箇所を返す なければ0. if(r == -1) r = n; int l = siz; r += siz; vector ls,rs; while(l < r){ if(l&1) ls.push_back(l++); if(r&1) rs.push_back(--r); l >>= 1; r >>= 1; } S1 okr = e(); for(int i=0; i=0; i--){ r = ls.at(i); S1 now = op(dat.at(r),okr); if(!f(now)){ while(r < siz){ r <<= 1; r++; now = op(dat.at(r),okr); if(f(now)){okr = now; r--;} } return r+1-siz; } okr = now; } return 0; } }; using S2 = pair; class SegmentTree2{ public: int siz = -1,n = -1; vector dat; S2 op(S2 a, S2 b){return {min(a.first,b.first+a.second),a.second+b.second};} S2 e(){return {0,0};} void renew (S2 &a,S2 x){ a = op(a,x); //a = x; //set(pos,x)で可能. //その他. } SegmentTree2(int N){init(N);} SegmentTree2(const vector &A){//長さ配列サイズに合わせる. siz = 1; n = A.size(); while(siz < n) siz *= 2; dat.resize(siz*2,e()); for(int i=0; i0; i--) dat.at(i) = op(dat.at(i*2),dat.at(i*2+1)); } void init(int N){ //全要素単位元に初期化. siz = 1; n = N; while(siz < n) siz *= 2; dat.assign(siz*2,e()); } void init(const vector &A){//長さ配列サイズに合わせる. siz = 1; n = A.size(); while(siz < n) siz *= 2; dat.resize(siz*2,e()); for(int i=0; i0; i--) dat.at(i) = op(dat.at(i*2),dat.at(i*2+1)); } void set(int pos,S2 x){ pos = pos+siz; dat.at(pos) = x; while(pos != 1){ pos = pos/2; dat.at(pos) = op(dat.at(pos*2),dat.at(pos*2+1)); } } void update(int pos,S2 x){ pos = pos+siz; renew(dat.at(pos),x); while(pos != 1){ pos = pos/2; dat.at(pos) = op(dat.at(pos*2),dat.at(pos*2+1)); } } S2 findans(int l, int r){ S2 retl = e(),retr = e(); l += siz,r += siz; while(l < r){ if(l&1) retl = op(retl,dat.at(l++)); if(r&1) retr = op(dat.at(--r),retr); l >>= 1; r >>= 1; } return op(retl,retr); } S2 get(int pos){return dat.at(pos+siz);} S2 rangeans(int l, int r){return findans(l,r);} S2 allrange(){return dat.at(1);} //rightは) leftは[で 渡す&返す. int maxright(const function f,int l = 0){ //fを満たさない最小の箇所を返す なければn. l += siz; int r = n+siz; vector ls,rs; while(l < r){ if(l&1) ls.push_back(l++); if(r&1) rs.push_back(--r); l >>= 1; r >>= 1; } S2 okl = e(); for(int i=0; i=0; i--){ l = rs.at(i); S2 now = op(okl,dat.at(l)); if(!f(now)){ while(l < siz){ l <<= 1; now = op(okl,dat.at(l)); if(f(now)){okl = now; l++;} } return l-siz; } okl = now; } return n; } int minleft(const function f,int r = -1){ //fを満たす最小の箇所を返す なければ0. if(r == -1) r = n; int l = siz; r += siz; vector ls,rs; while(l < r){ if(l&1) ls.push_back(l++); if(r&1) rs.push_back(--r); l >>= 1; r >>= 1; } S2 okr = e(); for(int i=0; i=0; i--){ r = ls.at(i); S2 now = op(dat.at(r),okr); if(!f(now)){ while(r < siz){ r <<= 1; r++; now = op(dat.at(r),okr); if(f(now)){okr = now; r--;} } return r+1-siz; } okr = now; } return 0; } }; int main(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); int N,D; cin >> N >> D; vector> BA(N); for(auto &[b,a] : BA) cin >> a >> b; sort(BA.rbegin(),BA.rend()); BA.push_back({0,0}); vector X; for(auto [b,a] : BA) X.push_back(b%D); sort(X.begin(),X.end()); X.erase(unique(X.begin(),X.end()),X.end()); int n = X.size(); for(int i=0; i give(n+n); for(int i=1; i r) r += n; now += Z.rangeans(l+1,r+1); } } int pos = lower_bound(X.begin(),X.end(),b%D)-X.begin(); Z.update(pos,a),Z.update(pos+n,a),suma += a,now += (b-b2+D-1)/D*a; if(suma >= D) break; auto [dec,sum] = Z2.get(pos); dec -= a,sum -= a; Z2.set(pos,{dec,sum}),Z2.set(pos+n,{dec,sum}); pos = lower_bound(X.begin(),X.end(),b2%D)-X.begin(); tie(dec,sum) = Z2.rangeans(pos+1,pos+n); dec += X.at(pos+1)-X.at(pos); answer = min(answer,now+b2+min(0LL,dec)); } cout << answer << endl; }