2018년 1월 28일 일요일

12764 싸지방에 간 준하

12764 싸지방에 간 준하 https://www.acmicpc.net/problem/12764

사용자의 컴퓨터 시작 시간과 끝 시간이 주어진다.
사용자는 비어있는 자리중 가장 작은 번호부터 사용한다.
컴퓨터를 최소한으로 사용할 때 그 개수와 자리별 사용회수를 구하는 문제이다.

우선순위 큐로 해결 할 수 있다.
사용자를 시작시간 순으로 정렬시킨 후 우선순위 큐에서 종료되어 나오는 사용자들을 관리해 준다.
set으로 종료되어 나오는 사용자들의 자리 번호를 관리해주면 된다.

#include <cstdio>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
int N;
pair<intint> p[100001];
set<int> save;
int ans[100001];
int solve() {
    priority_queue<pair<intint>> pq;
    int size = 0;
    for (int n = 0; n < N; n++) {
        while (!pq.empty()) {
            if (-pq.top().first <= p[n].first) {
                save.insert(pq.top().second);
                pq.pop();
            }
            else break;
        }
        if (save.empty()) {
            pq.push({ -p[n].second, size });
            ans[size++]++;
        }
        else {
            auto idx = save.begin();
            pq.push({ -p[n].second, *idx });
            ans[*idx]++;
            save.erase(idx);
        }
    }
    return size;
}
int main() {
    scanf("%d"&N);
    for (int n = 0; n < N; n++scanf("%d%d"&p[n].first, &p[n].second);
    sort(p, p + N);
 
    int M = solve();
    printf("%d\n", M);
    for (int m = 0; m < M; m++printf("%d ", ans[m]);
    return 0;
}
cs


2018년 1월 21일 일요일

3111 검열

3111 검열 https://www.acmicpc.net/problem/3111

  1. T에 A가 없으면 알고리즘을 종료한다.
  2. T에서 처음 등장하는 A를 찾은 뒤, 삭제한다.
  3. T에 A가 없으면 알고리즘을 종료한다.
  4. T에서 마지막으로 등장하는 A를 찾은 뒤, 삭제한다.
  5. 1번으로 돌아간다.
문자열 T와 단어A가 주어질 때 위의 알고리즘으로 남아있는 문자열을 구하는 문제이다.

문자열 폭발 문제와 유사하지만 맨앞과 맨뒤의 단어를 번갈아가며 지운다는 점이 다르다. 

문자열 폭발은 실시간으로 폭발이 일어날 경우가 생기면 바로바로 지워주는 반면에
이 문제는 먼저 전처리로 폭발이 일어나는 과정을 처리하며 남는것은 우선 스택2개에 각각 넣어준다.

예를들어 단어 A가 abc이고 T가 abcaabcbc가 있다고 하면 다음과 같은 과정이 될것이다.

전처리 이후의 두 스택을 이용해서 잘 해결하면 된다.
스택을 이용한다는 것보다는 전처리 이후에 처리한다는 구상을 떠올리기 어려운 문제였다.
#include <cstdio>
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)<(b)?(a):(b))
template<typename T>
struct stack {
    T *arr;
    int ptr;
    stack() :ptr(0) { arr = new T[300055]; }
    ~stack() { delete arr; }
    void clear() {
        ptr = 0;
    }
    void push(T data) {
        arr[ptr++= data;
    }
    void pop() {
        ptr--;
    }
    T top() {
        return arr[ptr - 1];
    }
    bool empty() {
        return ptr == 0;
    }
    int size() {
        return ptr;
    }
};
int N, M;
char cmp[26];
char revcmp[26];
char str[300001];
int main() {
    scanf("%s"&cmp);
    scanf("%s"&str);
    for (M = 0; cmp[M]; M++);
    for (N = 0; str[N]; N++);
    for (int m = 0; m < M; m++) revcmp[m] = cmp[M - m - 1];
 
    stack<char> stk1, stk2;
    int l = 0, r = N - 1;
    int dir = 1;
    while (l <= r && l < N && r >= 0) {
        bool find = true;
        if (dir) {
            stk1.push(str[l]);
            if (stk1.size() >= M) {
                for (int m = stk1.size() - M, t = 0; m < stk1.size(); m++, t++) {
                    if (cmp[t] != stk1.arr[m]) {
                        find = false;
                        break;
                    }
                }
                if (find) {
                    for (int m = 0; m < M; m++) stk1.pop();
                    dir ^= 1;
                }
            }
            l++;
        }
        else {
            stk2.push(str[r]);
            if (stk2.size() >= M) {
                for (int m = stk2.size() - M, t = 0; m < stk2.size(); m++, t++) {
                    if (revcmp[t] != stk2.arr[m]) {
                        find = false;
                        break;
                    }
                }
                if (find) {
                    for (int m = 0; m < M; m++) stk2.pop();
                    dir ^= 1;
                }
            }
            r--;
        }
    }
    if (stk1.empty() && !stk2.empty()) {
        for (int m = stk2.size() - 1; m >= 0; m--printf("%c", stk2.arr[m]);
        return 0;
    }
    if(!stk1.empty() && stk2.empty()) {
        for (int m = 0; m < stk1.size(); m++printf("%c", stk1.arr[m]);
        return 0;
    }
 
    bool find = true;
    while (find) {
        if (stk1.size() + stk2.size() < M) break;
        find = false;
        for (int n = max(0, stk1.size() - M); n < stk1.size(); n++) {
            int idx = 0, l = 0, r = 0;
            for (int m = n; m < stk1.size(); m++, l++) {
                if (stk1.arr[m] == cmp[idx]) idx++;
            }
            for (int m = stk2.size() - 1; m >= 0 && l + r < M; m--, r++) {
                if (stk2.arr[m] == cmp[idx]) idx++;
            }
            if (idx == M && l + r == idx) {
                find = true;
                for (int m = 0; m < l; m++) stk1.pop();
                for (int m = 0; m < r; m++) stk2.pop();
                break;
            }
        }
    }
    for (int m = 0; m < stk1.size(); m++printf("%c", stk1.arr[m]);
    for (int m = stk2.size() - 1; m >= 0; m--printf("%c", stk2.arr[m]);
    return 0;
}
cs

2018년 1월 4일 목요일

14603 소금과 후추(Large)

14603 소금과 후추(Large) https://www.acmicpc.net/problem/14603

행렬이 주어질 때 그 행렬을 $w$X$w$마다 압축하는 문제이다.
압축한다는것은 $w$X$w$행렬에서 중앙값을 추출한다는 것이다.

naive하게 생각하면 $O(nmw^{2})$이라 당연하게 시간초과가 발생할 것이다.

segment treeplane sweeping을 이용하면 $O(nmw$ $lg(k))$만에 해결할 수 있다.
($k$는 여기서 segment tree의 크기)

$n,m,w$가 각각 5,6,3이라면 위와 같이 훑어서 전체값을 구할 수 있다.
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
int N, M, L, K;
int arr[301][301];
vector<vector<int>> ans;
struct Segment {
    vector<int> tree;
    int size;
    Segment() {}
    Segment(int N) :size(N + 1) { tree.resize(4 * (N + 1), 0); }
    int update(int idx, int val, int node, int nl, int nr) {
        if (idx < nl || idx > nr) return tree[node];
        if (nl == nr) return tree[node] += val;
        int mid = (nl + nr) >> 1;
        return tree[node] = update(idx, val, node * 2, nl, mid) + update(idx, val, node * 2 + 1, mid + 1, nr);
    }
    void update(int idx, int val) {
        update(idx, val, 10size - 1);
    }
    int query(int val, int node, int nl, int nr) {
        if (nl == nr) return nl;
        int mid = (nl + nr) >> 1;
        if (tree[node * 2>= val) return query(val, node * 2, nl, mid);
        else return query(val - tree[node * 2], node * 2 + 1, mid + 1, nr);
    }
    int query(int val) {
        return query(val, 10size - 1);
    }
};
int main() {
    scanf("%d%d%d%d"&N, &M, &L, &K);
    Segment seg(L);
    for (int n = 0;n < N;n++for (int m = 0;m < M;m++scanf("%d"&arr[n][m]);
    for (int n = 0;n < K;n++for (int m = 0;m < K;m++)seg.update(arr[n][m], 1);
    for (int n = 0;n < N - K + 1;n++) {
        if (!(n & 1)) {        // ->
            vector<int> tmp;
            if (n != 0)
                for (int m = 0;m < K;m++) seg.update(arr[n - 1][m], -1), seg.update(arr[n + K - 1][m], 1);
            for (int m = 0;m < M - K + 1;m++) {
                if (m == 0) {}
                else {
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m - 1], -1);
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m + K - 1], 1);
                }
                tmp.push_back(seg.query((K*+ 1/ 2));
            }
            ans.push_back(tmp);
        }
        else {            // <-
            vector<int> tmp;
            for (int m = M - K;m < M;m++) seg.update(arr[n - 1][m], -1), seg.update(arr[n + K - 1][m], 1);
            for (int m = M - K;m >= 0;m--) {
                if (m == M - K) {}
                else {
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m + K], -1);
                    for (int y = n;y < n + K;y++) seg.update(arr[y][m], 1);
                }
                tmp.push_back(seg.query((K*+ 1/ 2));
            }
            reverse(tmp.begin(), tmp.end());
            ans.push_back(tmp);
        }
    }
    for (auto &n : ans) {
        for (auto &m : n) printf("%d ", m);
        printf("\n");
    }
    return 0;
}
cs

푸니까 1초제한에서 888ms가 나와서 조금 찝찝하긴 하지만 다른분들 풀이랑 별 차이가 없는듯 하다.

2018년 1월 3일 수요일

2853 배

2853 배 https://www.acmicpc.net/problem/2853

1일날에 모든 배가 들어온다.
그 후 한척이라도 배가 들어오면 이 날을 기록하였다.
배가 주기적으로 온다고 했을 때 방문한 배의 최소 개수를 구하는 문제이다.

수열에서 등차수열을 만족하는 최소 개수를 구하는 문제와 동치가 된다.
greedy하게 해결하였다.
index를 1부터 시작시킬 때 만약 해당 수열을 이미 방문했다면 건너 뛰고 아니라면
그에 해당하는 등차수열들을 방문한다.
#include <cstdio>
#include <algorithm>
#include <unordered_map>
using namespace std;
int N;
int arr[5001];
bool chk[5001];
unordered_map<int,int> save;
int ans, cnt;
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++scanf("%d"&arr[n]), save[arr[n]] = n;
    chk[0= true;
    for (int n = 1;n < N;n++) {
        if (chk[n]) continue;
        int diff = arr[n] - 1;
        ans++;
        for (int m = arr[n];m <= arr[N - 1];m += diff) {
            int idx = save[m];
            if (!chk[idx]) {
                chk[idx] = true;
                cnt++;
            }
        }
        if (cnt == N - 1break;
    }
    printf("%d\n", ans);
    return 0;
}
cs