Processing math: 100%
본문 바로가기

PS/BOJ

LARC(Latin America Regional Contests) 2018

반응형

https://www.acmicpc.net/category/detail/1956

 

Latin America Regional Contests 2018

 

www.acmicpc.net

22.09.16

전체 A~M 13문제 (FGIJK 제외 8솔)

아주 오랜만에 백준에서 문제를 풀었다.

최근 한 달간 한 것은 없지만 생각보다 바쁘게 살게 되었고, PS에 관심을 덜 가지게 되었다.

작년 ICPC 팀원들은 사정이 있어 올해 ICPC에는 참여하지 못하였고,

새로운 팀원 g3gogogo 그리고 cocjcr0208과 함께 하게 되었다.

새 학기 첫 연습으로 bnb2011 형님이 추천해준 셋 중 쉬운 셋을 골랐다.

첫 연습치고 괜찮은 결과지만 페널티 관리가 조금 아쉬웠다.

많이 연습을 진행해보고 공부하면 좋은 팀이 될 것 같아 기대된다.


A. A Symmetrical Pizza

문제

주어진 수(소수점 2자리까지 허용)에 임의의 수를 곱해서 360의 배수를 만들 수 있을 때, 가능한 수들 중 가장 작은 값을 구하여라

 

풀이

입력받은 수에 *100을 한 후 1~36000까지 돌면서 곱해보고 그 수가 36000으로 나누어떨어지는지 확인한다.

뭔가 더 간단한 풀이가 있을 것 같기는 하다.

 

소스코드

더보기
#include <bits/stdc++.h>
using namespace std;

int main(){
    int a, b;
    scanf("%d.%d",&a,&b);
    a=a*100+b;
    for(int i=1; i<=36000; i++){
        if(a*i%36000==0) printf("%d",i), exit(0);
    }
}

B. Building a Field

문제

원형으로 놓인 나무들이 있고 그 사이사이의 간격이 주어진다. 네 개의 나무를 골라 직사각형을 만들 수 있는지 구하라

 

풀이

모든 간격을 합한 값 S에 대해 S/2의 간격을 가진 두 나무 쌍이 2개 이상 있으면 직사각형을 만들 수 있다.

투포인터로 O(n)에 충분히 구해진다.

 

소스코드

더보기
#include <bits/stdc++.h>
using namespace std;
vector<int> v;
int n, c, s;
int main(){
    scanf("%d",&n); v.resize(n*2);
    for(int i=0; i<n; ++i) scanf("%d",&v[i]), v[i+n]=v[i], c+=v[i];
    if(c%2) puts("N"), exit(0);
    c/=2;

    int ch=0;
    for(int i=0, j=0; i<n; ++i){
        if(i) s-=v[i-1];
        for(;j<2*n && s<c; s+=v[j++]);
        if(s==c) ch++;
    }
    puts(ch>2?"Y":"N");
}

C. Cheap Trips

문제

주어지는 순서대로 여행을 갈 건데 120분 간격 안에서 시작되는 첫 여행은 100%, 두번째 여행은 50%, 3~6번째 여행은 25%, 그 이후는 다시 100%의 비용을 낸다. 여행의 첫 시작과 여행 사이사이에 딜레이를 자유롭게 줄 수 있을 때, 모든 여행에 드는 최소 비용은 얼마인지 구하라

 

풀이

g3gogogo가 해결했다.

7번째 이후의 여행은 모두 첫 여행으로 바꾸어 주면 된다. 각 여행에 대해 시작 딜레이(0~120)과 몇 번째 여행인지(1~6)을 기록해두는 DP를 이용하여 구하면 된다.


D. Database of Clients

문제

이메일을 받는데, 이메일의 local-part와 domain에 따라 사람을 구분할 수 있다.

local-part에서 두 가지 기준을 가진다.

- '.'은 무시한다.

- '+'이후의 문자열은 무시한다.

domain에서는 한 가지 기준을 따른다.

- domain이 다르면 다른 사람이다.

ex) bob@hello.com과 b.o.b@hello.com은 같은 사람이지만 bob@hellocom은 다른 사람이다.

이때, 주어진 이메일 주소들에서 다른 사람이 몇 명인지 구하여라

 

풀이

문자열 파싱을 잘 하면 된다..?

 

소스코드

더보기
#include <bits/stdc++.h>
using namespace std;
char S1[110], S2[110];
vector<pair<string,string>> mp;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; ++i){
        scanf("\n%[^@]@%s",S1,S2);
        string s1, s2;

        for(int j=0; S1[j]; ++j){
            if(S1[j]=='.') continue;
            if(S1[j]=='+') break;
            s1.push_back(S1[j]);
        }
        for(int j=0; S2[j]; ++j) s2.push_back(S2[j]);
        mp.push_back({s1,s2});
    }
    sort(mp.begin(),mp.end());
    int c=1;
    for(int i=1; i<mp.size(); ++i){
        if(mp[i].first==mp[i-1].first && mp[i].second==mp[i-1].second) continue;
        ++c;
    }
    printf("%d",c);
}

E. Escape, Polygon!

문제

기하도망쳐!!

10^5각 이하의 볼록 다각형을 입력받는다. 다각형의 세 변을 직선으로 생각할 때, 직선들로 만들어진 영역에 대해 다음과 같이 구분할 수 있다.

(a)는 다각형을 포함하며, (b), (c)는 다각형을 포함하지 못한다.

다각형을 포함하는 세 직선을 선택하는 방법의 수를 구하라

 

풀이

다각형의 임의의 변으로 만들 수 있는 직선을 x라 하자.

x'은 x와 평행한 점의 개수이다.

v1x는 x~x'사이의 x'을 포함하지 않는 반시계 방향의 변 개수이다.

v2x x~x'사이의 x'을 포함하는 반시계 방향의 변 개수이다.

Vx는 0~x사이의 변들에 대한 v1x의 누적합이다.

 

특정 직선 A가 아래 변이라 가정하자. 만들 수 있는 삼각형의 개수는 다음과 같은 식으로 표현된다.

ans=(Va+v1a+1Va+1)+(v1a×(v1a1)2)(v1a×(v2a1))

모든 직선에 대해 해당 값을 누적한 후 3으로 나누어주면 답을 구할 수 있다.

 

소스코드

더보기
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const ll N = 2e5+10;
ll v[N], v2[N], V[N];
vector<pii> e;

ll ccw(pii a1, pii a2, pii b1, pii b2){
    b2.first = b2.first - b1.first + a2.first;
    b2.second = b2.second - b1.second + a2.second;
    auto&[x1,y1] = a1;
    auto&[x2,y2] = a2;
    auto&[x3,y3] = b2;
    ll temp = x1*y2+x2*y3+x3*y1 -y1*x2-y2*x3-y3*x1;
    if (temp > 0) return 1;
    else if (temp < 0) return -1;
    else return 0;
}

int main(){
    ll n;
    scanf("%lld",&n); e.resize(n*2+1);
    for(ll i=0; i<n; ++i){
        auto&[x,y] = e[i];
        scanf("%lld%lld",&x,&y); e[i+n] = e[i];
    } e[n*2] = e[0];

    for(ll i=0, j=1, j2=1; i<n; ++i){
        for(; j<2*n && ccw(e[i],e[i+1],e[j],e[j+1])>0; ++j);
        for(; j2<2*n && ccw(e[i],e[i+1],e[j2],e[j2+1])>=0; ++j2);
        v[i] = j-i-1;
        v2[i] = j2-i-1;
        if(i) V[i] = V[i-1] + v[i-1];
    }
    for(ll i=n; i<n*2; ++i) v[i] = v[i-n], V[i] = V[i-1] + v[i-1];

    ll pr=0;
    for(ll i=0; i<n; ++i) pr+=V[i+v[i]+1]-V[i+1] + v[i]*(v[i]-1)/2 - v[i] * (v2[i]-1);
    printf("%lld",pr/3);
}

F. Fantastic Beasts

문제

BAPC 2019의 Deck Randomisation과 비슷한 문제인 것 같다.


G. Gathering Red-Black Fruits


H. Highway Decommission

문제

최소가중치 다익스트라를 구하면 된다.

 

풀이

주어진 그래프에서 루트(노드 1번)과 모든 정점 사이의 최단경로를 이루는 간선을 구하되, 가능하면 최소 가중치 간선을 선택해야한다. pq를 사용하여 다익스트라를 잘 구현하면 해결할 수 있다.

 

소스코드

더보기
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 11000;
ll vis[N], d[N];
struct edge{ll p, l, c;};
struct cmp{
    bool operator()(edge &a, edge &b){
        return a.l>b.l || (a.l==b.l && a.c>b.c);
    }
};
vector<edge> e[N];
vector<edge> cnd[2];

ll pr;
int main(){
    ll n, m;
    scanf("%lld%lld",&n,&m);
    for(ll i=0; i<m; ++i){
        ll x, y, l, c;
        scanf("%lld%lld%lld%lld",&x,&y,&l,&c);
        e[x].push_back({y,l,c});
        e[y].push_back({x,l,c});
    }
    priority_queue<edge,vector<edge>,cmp> pq;
    vis[1]=1;
    for(ll now=1, vcnt=1; vcnt<n;){
        for(auto&[p,l,c]:e[now]){
            if(vis[p]) continue;
            pq.push({p,l+d[now],c});
        }
        while(!pq.empty()){
            auto [p,l,c] = pq.top(); pq.pop();
            if(vis[p]) continue;
            vis[p]=1; d[p]=l;
            now = p;
            pr+=c;
            ++vcnt;
            break;
        }
    }
    printf("%lld",pr);
}

I. https://www.acmicpc.net/problem/16529

문제

주어진 방향그래프에서 졸라맨을 최대 몇 개 그릴 수 있는지 구하여라

 

풀이

졸라맨은 머리 하나, 상체 하나(손 두 개&하체 한 개 달림), 하체 하나(발 두 개 달림)로 이루어져 있다.

실상 머리와 손, 발들은 같은 역할이므로 각 노드는 3개의 상태를 가질 수 있다.

1. 상체

2. 하체

3. 머리(혹은 손,발)

이를 잘 이용하여 tree에서의 dp를 구하면 되는데, 구현이 꼬여서 연습때는 해결하지 못했다.


J. Jeopardized Election

문제

V(odd)개의 길이 C의 sequence S(Si, 1iC)가 주어진다.

임의의 길이 C인 sequence A를 하나 만들어보자.

A1부터 시작하여 다음의 작업을 반복한다.

1. S들의 첫 자리가 Ai인 케이스의 수 C'에 대해 C<C/2라면 S들에서 Ai를 모두 지운다.

2. C>C/2라면 해당 값 ANS을 선택 후 종료한다.

원하는 ANS가 선택되도록 A를 만들어라


K. https://www.acmicpc.net/problem/16531

문제

N개의 수가 있다. 이를 잘 조합하여 2N개의 수들을 얻었다.

우리는 이 2N개의 수를 알 때, 원래의 N개 수를 구해야 한다.

 

풀이

모두 합했을 때, 2N1×()임을 알 수 있다.

그리고, 모르겠다 :)


L. Looking for the Risk Factor

문제

Q개의 쿼리가 주어진다. 각 쿼리는 두 개의 수 N과 K로 이루어지는데,

2 이상 N 이하의 수들 중 K보다 작은 인수들로만 이루어진 수의 개수를 구해야 한다.

 

풀이

오프라인 쿼리 문제이다.

에라토스테네스의 체로 전처리O(NlogNlogN)를 한 후,

모든 값들에 대해 자신의 가장 큰 소인수에 해당하는 벡터에 기록한다.

쿼리에 들어온 값 이하의 모든 소인수들에 대해 이분탐색으로 개수를 구한다.

수행시간이 1.7초인 것을 보아 정해는 아니지만, 충분히 잘 돌아가는 해답인 것 같다.

 

소스코드

더보기
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int chk[N], no;
vector<int> insu[N];
map<int,int> mp;
vector<vector<int>> vec;
int main(){
    mp[2]=no++;
    for(int j=2; j<N; j+=2) chk[j]=1, insu[j].push_back(2);
    for(int i=3; i<N; ++i){
        if(chk[i]) {mp[i] = no-1; continue;}
        mp[i]=no++;
        for(int j=i; j<N; j+=i) chk[j]=1, insu[j].push_back(i);
    }
    vec.resize(no);
    for(int i=2; i<N; ++i) vec[mp[insu[i][insu[i].size()-1]]].push_back(i);
    
    int T;
    scanf("%d",&T);
    for(int n, k; T--;){
        int p=0;
        scanf("%d%d",&n,&k);
        for(int j=mp[k]; j>=0; --j){
            p+=upper_bound(vec[j].begin(),vec[j].end(),n)-vec[j].begin();
        }
        printf("%d\n",p);
    }
}

M. Mount Marathon

문제

무언가 쉬운 문제였다고 한다.

 

풀이

cocjr0208씨가 해결하였다.

반응형

'PS > BOJ' 카테고리의 다른 글

NWERC(Northwestern Europian Regional Contest) 2021  (0) 2022.10.01
NANC(North Central North America) 2020  (4) 2022.09.25
SWERC 2018  (0) 2022.07.11
GCPC 2020  (0) 2022.05.25
BAPC 2021  (0) 2022.05.09