사용 알고리즘
Lazy propagation + binary search
풀이
학생들이 어떻게 이동하는지를 관찰해보자. 중앙을 기준으로 왼쪽에 있는 학생들은 무조건 1번 쪽으로 이동하고 오른쪽에 있는 학생들은 무조건 N번 쪽으로 이동할 것이다.
그리고 한 학생이 빠지게 되면 중간이 비고 2개의 연속된 그룹이 생기는데, 이 그룹끼리는 절대 서로 영향을 주지 않는다. (즉 한쪽 그룹에서 학생이 빠졌는데 다른 그룹의 학생이 이동하는 일은 없다는 뜻이다)
따라서 한명이 일단 빠졌다고 하자. 이 한명이 어떤 형태로 빠지는지는 N이 홀수인 경우와 짝수인 경우로 나눠 관찰하면 쉽게 찾을 수 있다.
이제 필요한 관찰은 다음과 같다.
1. 어떤 학생이 빠질 때 학생들의 번호는 항상 순서대로 유지된다.
2. 어떤 학생이 빠질 때 학생들은 항상 인접해 있다.
예를 들어 현재 '1 2 3 0 0 0 6 8' 인 상황이라고 하자. (같은 색은 같은 그룹이다)
여기서 2가 빠지면 어떻게 될까?
'1 3 0 0 0 0 6 8'이 될것이다. 1과 3의 순서가 바뀌지 않았으므로 1번 조건도 만족하고, 그룹끼리 인접해 있으므로 2번도 만족한다.
위 2개의 관찰을 기준으로 하나만 더 관찰하면 된다.
'1 2 3 4 5 6 7 8 9 0 0 0 0 ... 0 0 0'에서 0이 충분히 많다고 하고 (9개보단 많다) 4가 빠진다고 하자. 이때 1,2,3이 움직일 일은 절대 없을 것이다. 그렇다면 5 6 7 8 9는 어떻게 움직이는게 최적일까? 당연히 통째로 왼쪽으로 1칸을 이동하는 형태일 것이다. 따라서 없어지는 번호를 기준으로 그 뒷쪽이 통째로 옮겨져야 한다.
이러한 경우를 반대 그룹에서도 똑같이 생각하면 된다. 따라서 다음과 같은 처리를 할 수 있으면 된다.
1. 버스에서 x번 위치에 있는 학생의 번호를 찾는다.
2. x번인 학생이 버스에서 위치하는 번호를 찾는다.
대충 이런식의 처리를 해줄 수 있으면 위 조건에 맞춰 이분탐색으로 적절히 처리해줄 수 있다. 이렇게 처리하면 구간 갱신을 해줘야 되서 레이지 세그먼트 트리가 2개 필요하다.(학생 번호를 관리하는 세그 / 버스 위치를 관리하는 세그) 다만 알고리즘 분류에 레이지가 없는 걸로 봐서 이게 정해는 아닌 듯 하다.
#include <iostream>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <cstring>
#include <vector>
#include <string>
#include <climits>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <bitset>
#include <cassert>
#include <list>
using namespace std;
typedef long long ll;
#define fast ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define all(x) x.begin(), x.end()
#define rll(x) x.rbegin(), x.rend()
#define comp(x) x.erase(unique(all(x)), x.end())
#define MOD 998244353
#define yes cout<<"YES\n"
#define no cout<<"NO\n"
#define inf 1e13
ll n, m;
ll g1,g2;
vector<pair<ll,ll>> qry;
struct lazyseg{
struct Node{
ll diff, lazy;
};
vector<Node> tree;
lazyseg(): tree(4040404) {}
void propagate(ll node, ll s, ll e){
if(!tree[node].lazy)return;
auto [diff, lazy] = tree[node];
tree[node].diff += (e-s+1)*lazy;
if(s!=e)tree[node*2].lazy += lazy, tree[node*2+1].lazy += lazy;
tree[node].lazy=0;
}
void upd(ll node, ll s, ll e, ll l, ll r, ll diff){
if(l>r)return;
propagate(node,s,e);
if(e<l or r<s)return;
if(l<=s and e<=r){
tree[node].diff += (e-s+1)*diff;
if(s==e)return;
tree[node*2].lazy += diff;
tree[node*2+1].lazy += diff;
return;
}
ll mid = s+e>>1;
upd(node*2, s, mid, l, r, diff); upd(node*2+1, mid+1, e, l, r, diff);
tree[node].diff = tree[node*2].diff+tree[node*2+1].diff;
}
void upd(ll l, ll r, ll diff){ upd(1,1,n,l,r,diff); }
ll query(ll node, ll s, ll e, ll l, ll r){
if(l>r)return 0;
propagate(node,s,e);
if(e<l or r<s)return 0;
if(l<=s and e<=r)return tree[node].diff;
ll mid = s+e>>1;
return query(node*2,s,mid,l,r)+query(node*2+1,mid+1,e,l,r);
}
ll query(ll l, ll r){ return query(1,1,n,l,r); }
} seg, idxseg;
int main(){
fast;
cin>>n>>m;
idxseg.upd(1,n,1);
seg.upd(1,n,1);
qry.resize(m);
for(auto &[a,b] : qry)cin>>a>>b;
ll idx = 0;
for(; idx < m ; idx++){
if(qry[idx].first==1){
idxseg.upd(qry[idx].second,qry[idx].second,-1);
if(n&1){
bool is_L = (qry[idx].second <= n/2+1);
if(is_L){
seg.upd(qry[idx].second,qry[idx].second,-1);
seg.upd(qry[idx].second+1, n/2+1, -1);
seg.upd(qry[idx].second, n/2, 1);
g1 = n/2, g2 = n/2+2;
}
else{
seg.upd(qry[idx].second,qry[idx].second,-1);
seg.upd(n/2+1,qry[idx].second-1,-1);
seg.upd(n/2+2,qry[idx].second,1);
g1 = n/2, g2 = n/2+2;
}
}
else{
bool is_L = (qry[idx].second <= n/2);
if(is_L){
seg.upd(qry[idx].second,qry[idx].second,-1);
seg.upd(qry[idx].second+1, n/2, -1);
seg.upd(qry[idx].second, n/2-1, 1);
g1 = n/2-1, g2 = n/2+1;
}
else{
seg.upd(qry[idx].second,qry[idx].second,-1);
seg.upd(n/2+1,qry[idx].second-1,-1);
seg.upd(n/2+2,qry[idx].second,1);
g1 = n/2, g2 = n/2+2;
}
}
break;
}
else cout<<qry[idx].second<<"\n";
}
idx++;
for(; idx < m ; idx++){
auto [q, num] = qry[idx];
if(q==1){
ll sumx = idxseg.query(1,num);
ll l = 0, r = n+1;
while(l+1 < r){
ll mid = l+r>>1;
if(sumx <= seg.query(1,mid))r = mid;
else l = mid;
}
ll IDX = r; //num번 학생 위치
idxseg.upd(num,num,-1);
if(seg.query(1,IDX)<=g1){
seg.upd(IDX,IDX,-1);
seg.upd(IDX+1, g1, -1);
seg.upd(IDX, g1-1, 1);
g1--;
}
else{
seg.upd(IDX,IDX,-1);
seg.upd(g2,IDX-1,-1);
seg.upd(g2+1,IDX,1);
g2++;
}
}
else{
if(seg.query(num,num)==0){
cout<<0<<"\n";
continue;
}
ll sumx = seg.query(1,num);
ll l = 0, r = n+1;
while(l+1 < r){
ll mid = l+r>>1;
if(sumx <= idxseg.query(1,mid))r = mid;
else l = mid;
}
cout<<r<<"\n";
}
}
}
어렵게 풀었는지 맞은 코드 중 코드도 긴편이고 속도도 느린 편이다.
'백준 > 플래티넘' 카테고리의 다른 글
BOJ 21099 - Four XOR (P4) (1) | 2024.02.16 |
---|---|
BOJ 8452 - 그래프와 쿼리 (P1) (1) | 2023.12.23 |
BOJ 25639 - 수열과 최대 상승 쿼리 (P1) (0) | 2023.08.14 |
BOJ 1201 - NMK (P3) (0) | 2023.07.22 |
BOJ 4013 - ATM (P2) (0) | 2023.07.20 |