본문 바로가기

정보올림피아드&알고리즘

카드 배열

카드 배열

문제

하나의 숫자가 쓰여 있는 카드가 N장이 있다. 쓰여 있는 숫자는 0부터 N-1사이의 숫자 중 하나이며, 모든 카드의 숫자는 서로 다르다.



이 카드들 중에서 k개의 카드를 선택하여 배열한 순서를 CK-1, CK-2, ..., C0라고 하자. 

이렇게 배열된 k장의 카드가 N진법의 수를 나타낸다고 하면, 카드 Ci는 N진법의 수에서의 한 자리수를 의미하며 그 자릿수의 값은 Ci×Ni이다.

그러므로 배열된 카드의 수의 값은 Ck-1×Nk-1+Ck-2×Nk-2+...+C0×N0이 된다.



배열되는 카드들 사이에는 Ci>Cj형태의 제약조건이 주어진다. 제약조건 Ci>Cj는 Ci숫자가 Cj숫자보다 커야함을 의미한다. 단, i≠j.



예를 들어, N=4, k=3인 경우에 C2>C0, C0>C1의 제약조건이 주어졌다고 하자. 

이 경우 가능한 카드의 배열은 (2, 0, 1), (3, 1, 2), (3, 0, 1), 그리고 (3, 0, 2) 네 가지 경우가 있다.



이 네 개의 경우들 중에서 가장 큰 수가 되는 카드의 배열은 (3, 1, 2)이고 이 수의 값은 3×42+1×41+2=54이다. 

또한, 가장 작은 수가 되는 카드 배열은 (2, 0, 1)이고 이 수의 값은 2×42+0×41+1=33이다.



전체 카드의 수와 선택할 카드의 수 그리고 제약조건들이 주어질 때, 

제약조건을 만족하는 카드배열 중에서 가장 큰 값을 갖는 카드배열과 가장 작은 값을 갖는 카드배열을 찾아서 

그 값의 차이를 구하는 프로그램을 작성하시오.

 

 

입력형식

입력의 첫 번째 줄에는 전체 카드의 수를 나타내는 N과 선택하는 카드의 수 k와 제약조건의 수 P가 하나의 빈칸을 사이에 두고 주어진다. 

단, 3≤k≤300,000, 0≤P≤1,000,000이다.

두 번째 줄부터 P개의 줄에는 각 줄마다 하나의 제약조건을 나타내는 두 개의 정수 a,b가 하나의 빈칸을 사이에 두고 주어진다. 이는 제약조건 Ca>Cb를 의미한다.

 

출력형식

제약조건을 만족하는 카드배열 중에서 가장 큰 값을 갖는 카드배열과 가장 작은 값을 갖는 카드배열을 찾고, 

그 값의 차이를 1,000,000,007로 나눈 나머지를 출력한다. 

  단, 제약조건을 만족하는 카드의 배열이 존재하지 않는 경우는 없다.

 

[테스트 데이터 조건]

• 전체 테스트 데이터의 20%는 N, k≤11.

• 전체 테스트 데이터의 40%는 N, k≤10,000.

 

입력 예

4 3 2

2 0

0 1

출력 예

21

 

소스코드

더보기

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <algorithm>

#define MAXN 300000
#define MAXM 1000000
#define TreeN 1048576
#define from Q[Head];


struct List {
    int a, b;    // a > b
};
int N, M, K;
List A[MAXM + 1];                //크기관계 저장
int S[MAXN + 1], F[MAXN + 1];    // 시적점에 대한 리스트 첫 좌표, 끝 좌표
int C[MAXN + 1], Cp[MAXN + 1];   // degree or node
int T[TreeN], Ti;                // 우선순위 큐 indexed tree 구현
int Result_s[MAXN + 1];          // 최소 결과값
int Result_b[MAXN + 1];          // 최대 결과값

bool SortF(List a, List b)
{
    return a.a < b.a;
}

void Input()
{
    int i;
    scanf("%d %d %d", &N, &K, &M);
    
    for (i = 1; i <= M; i++)
    {
        scanf("%d %d", &A[i].a, &A[i].b);
        // 번호 앞족부터 1로 시작하도록 변경
        A[i].b = K - A[i].b;
        A[i].a = K - A[i].a;
        
        C[A[i].b]++;
    }
    std::sort(A+1, A+M+1, SortF);

    for (i = 1; i <= M; i++)
    {
        if (A[i].a != A[i - 1].a)
            S[A[i].a] = i;

        F[A[i].a] = i;
    }
}

void Push(int x) {      // 우선순위 큐에 원소 추가
    T[Ti + x] = x;
    x += Ti;

    while (1)
    {
        x /= 2;
        if (x == 0)
            break;

        if (T[x * 2] < T[x * 2 + 1])
            T[x] = T[x * 2 + 1];
        else
            T[x] = T[x * 2];
    }
}

void Delete(int x) {    // 우선순위 큐에 원소 삭제
    T[Ti + x] = 0;
    x += Ti;

    while (1)
    {
        x /= 2;
        if (x == 0)
            break;

        if (T[x * 2] < T[x * 2 + 1])
            T[x] = T[x * 2 + 1];
        else
            T[x] = T[x * 2];
    }
}

void Precess(int c, int* Result)
{
    int i, n, tmp, dir;

    if (c == 0)
    {
        n = K - 1;
        dir = -1;
    }
    else
    {
        n = N - K;
        dir = 1;
    }

    Ti = 1;
    while (1)
    {
        if (Ti >= K)
            break;
        Ti *= 2;
    }

    Ti--;       // indexed tree 세팅

    for (i = 1; i <= K; i++)
    {
        if (C[i] == 0)       // 단말노드 push
            Push(i);
    }

    while (1)
    {
        if (T[1] == 0)
            break;
        tmp = T[1];
        Delete(T[1]);      // pop
        Result[tmp] = n;
        n += dir;

        if (S[tmp])       // 연결된 간선이 있을 때
        {
            for (i = S[tmp]; i <= F[tmp]; i++)
            {
                C[A[i].b]--;
                if (C[A[i].b] == 0)
                {
                    Push(A[i].b);
                }
            }
        }
    }
}

void Output()
{
    int i;
    __int64 bsum = 0, ssum = 0, tmp = 1;   // 리눅스 __int64_t

    for (i = K; i >= 1; i--)
    {
        ssum += Result_s[i] * tmp;
        bsum += Result_b[i] * tmp;
        tmp *= N;
        tmp %= 1000000007;
        ssum %= 1000000007;
        bsum %= 1000000007;
    }
    bsum -= ssum;

    if (bsum < 0)
        bsum += 1000000007;
    printf("%d", (int)bsum);
}

int main()
{
    int i, tmp;

    Input();

    std::sort(A + 1, A + M + 1, SortF);
    for (i = 1; i <= M; i++)
    {
        if (A[i].a != A[i - 1].a)
            S[A[i].a] = i;

        F[A[i].a] = i;
    }
    Precess(0, Result_s);    // 가장 작은 수 구하기

    for (i = 1; i <= K; i++)
        S[i] = F[i] = 0;

    for (i = 1; i <= M; i++)
    {
        tmp = A[i].a;
        A[i].a = A[i].b;
        A[i].b = tmp;

        C[A[i].b]++;
    }

    std::sort(A + 1, A + M + 1, SortF);
    for (i = 1; i <= M; i++)
    {
        if (A[i].a != A[i - 1].a)
            S[A[i].a] = i;

        F[A[i].a] = i;
    }
    Precess(1, Result_b);    // 가장 큰 수 구하기
    Output();
    return 0;
}

'정보올림피아드&알고리즘' 카테고리의 다른 글

버스 갈아타기  (0) 2022.03.17
아시아 정보올림피아드  (0) 2022.03.15
사회망 서비스  (0) 2022.03.15
먹이사슬  (0) 2022.03.14
회전 초밥  (0) 2022.03.14