NYPC 2024문제풀이
채점 및 문제는 BIKO를 통해 확인할 수 있다.
초밥
-
범위는 [1, min(A,B)] 이고, 탐색 범위가 크기 때문에 이분탐색을 떠올림
-
Binary jumping (추후 알고리즘에서 다룰 예정) 기법을 이용하여 문제풀이
#include <bits/stdc++.h>
using namespace std;
int check(int A, int B, int i){
return A+B <=4*i;
}
int main(void){
cin.tie(0)->sync_with_stdio(0);
int T; cin >> T;
while(T--){
int A,B;
cin >> A >> B;
int l = 0, r = min(A,B);
for(int k = r; k >=1;k>>=1){
while(l+k <= r && !check(A,B,l+k)) l += k;
}
cout << (l+1 > min(A,B) ? -1 : l+1)<<"\n";
}
return 0;
}
무한길이 물풍선
- 2개의 물풍선을 비교하는 경우에 $O(N^2)$가 걸리게 되는데, 이는 TLE
- Set을 이용하여 중복이 되는 경우에 답을 담는 set에 다시저장
- 위 방식은 $O(N\log N)$
#include <bits/stdc++.h>
using namespace std;
int main(void){
set <int> X,Y,ansX, ansY;
int N; cin >> N;
while(N--){
int x,y; cin >> x >> y;
if(X.find(x) == X.end()) X.insert(x);
else{
ansX.insert(x);
}
if(Y.find(y) == Y.end()) Y.insert(y);
else ansY.insert(y);
}
cout << ansX.size()+ansY.size() <<"\n";
return 0;
}
커닝시티 헤어샵
- D[i] : i번째까지의 최소 시간
- $D[i] = min(D[i-1]+ t[i], D[i-2] + max(t[i],t[i-1]))$
- 정수의 범위를 벗어날수 있으므로, long long 타입을 사용해야함..!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N;
ll D[505050];
ll t[505050];
int main(void){
cin >> N;
for(int i = 1;i<=N;i++) cin >> t[i];
D[1] = t[1];
for(int i = 2; i<=N;i++){
D[i] = min(D[i-1]+t[i], D[i-2]+ max(t[i],t[i-1]));
}
cout <<D[N]<<"\n";
return 0;
}
게임
- 핑크빈의 목표는 가장 앞에있는 1을 없애서 작은값으로
-
블랙빈의 목표는 가장 앞에있는 0을 없애서 큰값으로
- Deque를 이용하여 front 또는 back를 접근할수 있다.
#include <bits/stdc++.h>
using namespace std;
int main(void){
int N, K;
deque <int> Z[2];
string s;
cin >> N >> K >> s;
for(int i = 0;i<N;i++) Z[s[i]-'0'].push_back(i);
while(K--){
if(!Z[1].empty()) Z[1].pop_front();
else Z[0].pop_back();
if(!Z[0].empty()) Z[0].pop_front();
else Z[1].pop_back();
}
for(int i = 0;i <N;i++){
if(s[i] == '0' &&!Z[0].empty() && Z[0].front() == i) {cout << '0'; Z[0].pop_front();}
if(s[i] == '1' &&!Z[1].empty() && Z[1].front() == i){cout << '1'; Z[1].pop_front();}
}
}
골드리치의 비밀금고
- Mo’s Algorithm과 Bitset을 이용하여 풀수 있음.
- cnt[x] 로 개수를 세주면서 1이 될때만 체크할수 있고,
- _Find_first() 로 첫번째 1이 되는 순간을 찾을 수 있다.
-$O(Q \times (\sqrt N + N/32))$ 의 시간복잡도를 얻을 수 있다.
-
Set이나 Priority_queue를 이용하여 구현해볼려했으나, $(O\log N \sqrt N)$ 으로 TLE가 나게 된다.
-
bitset이 생각보다 효율이 좋다는 것을 알게됨.
#include <bits/stdc++.h>
#include <queue>
using namespace std;
typedef long long ll;
int sz;
const int M = 300000;
struct Q{
int i,s,e;
bool operator < (Q &x){
if(s/sz != x.s/sz) return s/sz < x.s/sz;
return e < x.e;
}
};
vector<Q> query;
int v[303030],ans[303030], cnt[300001];
bitset<M> r;
void add(int x){
cnt[x]++;
if(cnt[x] == 1) r[x] = 1;
if(cnt[x] != 1) r[x] = 0;
}
void del(int x){
cnt[x]--;
if(cnt[x] == 1) r[x] = 1;
if(cnt[x] != 1) r[x] = 0;
}
int main(void){
cin.tie(0)->sync_with_stdio(0);
int n; cin >> n ;
sz = sqrt(n);
for(int i = 1; i<=n;i++) cin >> v[i];
int q; cin >> q;
for(int i = 0; i<q;i++){
int s,e; cin >> s >> e;
query.push_back({i,s,e});
}
sort(query.begin(), query.end());
int s = query[0].s, e = query[0].e;
for(int i = s ; i<=e;i++) {
add(v[i]);
}
ans[query[0].i] = r._Find_first();
for(int i = 1; i<q;i++){
while(s < query[i].s) del(v[s++]);
while(s > query[i].s) add(v[--s]);
while(e < query[i].e) add(v[++e]);
while(e > query[i].e) del(v[e--]);
ans[query[i].i] = r._Find_first();
}
for(int i = 0; i<q;i++) cout << (ans[i] == M ? 0 : ans[i]) <<"\n";
}
장비교체
- D[i]의 총합과 각 i 마다의 U[i]와 total을 비교하면서 최대한 장비 업그레이드를 할수 있는 방향으로 그리디하게 구현이 가능하다.
- $O(N)$
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll U[505050], D[505050], M[505050], N , total;
char ans[505050];
int main(void){
cin.tie(0)->sync_with_stdio(0);
cin >> N;
for(int i = 1; i<=N;i++) cin >> U[i];
for(int i = 1; i<=N;i++) cin >> D[i];
for(int i = 1; i<=N;i++) M[i] = max(0LL, D[i]);
for(int i = N; i>=1;i--) M[i] += M[i+1];
for(int i = 1; i<=N;i++) ans[i] = '0';
for(int i = 1;i<=N;i++){
if(U[i] != -1 && total+U[i] <= M[i+1]) {ans[i] = '+'; total += U[i];}
else if (D[i] != -1 && total > M[i+1]){ans[i] = '-'; total -= D[i];}
}
cout << ans+1;
}
루시드의 레이저 공격을 피해라!
- 선분교차 문제
- 선분이 축과 평행한 직선 또는 기울기가 1 또는 -1 인 직선 밖에 없으므로, binary_search 를 이용하여 문제를 풀 수 있음.
-
또한 기울기가 1 또는 -1 인 직선은 축을 45도 회전시켜, 직선처럼 만든 후에 풀 수 있음 (중요한 아이디어)
- 일반적인 선분교차로는 $O(QM)$ 으로 TLE
- $O(Q \log M + M \log M)$ 이 걸리게 된다.
#include <bits/stdc++.h>
using namespace std;
struct Point{
int X ,Y;
Point operator - (const Point &p){return {X-p.X, Y - p.Y};}
Point operator + (const Point &p){ return {X+p.X, Y+p.Y};}
bool operator <(const Point &p){return tie(X,Y) < tie(p.X, p.Y);}
bool operator >(const Point &p){return tie(X,Y) > tie(p.X, p.Y);}
bool operator == (const Point &p){return tie(X,Y) == tie(p.X, p.Y);}
bool operator >= (const Point &p){return tie(X,Y) >= tie(p.X, p.Y);}
bool operator <= (const Point &p){return tie(X,Y) <= tie(p.X, p.Y);}
};
Point Rotate(Point p){
return {p.X + p.Y , p.X - p.Y};
}
struct Line{
Point p1,p2;
};
vector<bool> f(vector<Line> laser, vector<Line> query){
vector<bool> ret(query.size(),1);
vector<int> lx,ly;
for(int i = 0; i<laser.size();i++){
auto [p1,p2] = laser[i];
if(p1.X == p2.X) lx.push_back(p1.X);
if(p1.Y == p2.Y) ly.push_back(p1.Y);
}
sort(lx.begin() , lx.end());
sort(ly.begin(), ly.end());
auto _on = [&](Point p) -> bool {
return binary_search(lx.begin(), lx.end(), p.X) || binary_search(ly.begin(),ly.end(), p.Y);
};
for(int i = 0; i<query.size();i++){
auto [p1,p2] = query[i];
if(_on(p1) || _on(p2)) ret[i] = false;
if(p1.X != p2.X){
int x1 = p1.X, x2 = p2.X;
if(x1 > x2) swap(x1,x2);
if(!(upper_bound(lx.begin(), lx.end(), x1) ==upper_bound(lx.begin(), lx.end(), x2))) ret[i] = false;
}
if(p1.Y != p2.Y){
int y1 = p1.Y, y2 = p2.Y;
if(y1 > y2) swap(y1,y2);
if(!(upper_bound(ly.begin(), ly.end(), y1) ==upper_bound(ly.begin(), ly.end(), y2))) ret[i] = false;
}
}
return ret;
}
int main(void){
cin.tie(0)->sync_with_stdio(0);
int N, M, Q;
cin >> N >> M >> Q;
vector <Line> laser;
vector <Line> query;
for(int i = 0;i<M;i++){
int x1,y1, x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
laser.push_back({ {x1,y1} , {x2,y2} });
}
for(int i = 0 ;i<Q;i++){
int x1,y1, x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
query.push_back({ {x1,y1} , {x2,y2} } );
}
auto r1 = f(laser,query);
for(auto &p : laser) {p.p1 = Rotate(p.p1); p.p2 = Rotate(p.p2);}
for(auto &p : query) {p.p1 = Rotate(p.p1); p.p2 = Rotate(p.p2);}
auto r2 = f(laser,query);
for(int i = 0;i<Q;i++)
cout << (r1[i] && r2[i]) <<"\n";
}