문제링크 : https://www.acmicpc.net/problem/2820


아이디어

  • 상사와의 관계를 인접 행렬로 관리

    • 업데이트 할경우 인접행렬을 업데이트
    • 시간복잡도 : O(M * N) : 2.5e11 > 시간초과
  • 레이지 개념 사용할 수 있을까?

    • a의 부하의 월급을 증가할떄 : a에 임시로 달아놓음
    • u로 출력할때 : 상사에서부터 lazy 업데이트 후 출력
    • 시간복잡도 : 편향트리일때 O(MN) > 시간초과
  • 자식 관계를 세그먼트 트리에 입력

    • 트리가 편향되어있을때 O(NM)을 세그먼트에 넣어 O(NlgM)으로 만듬
    • 세그먼트에 넣도록 노드를 배열에 각각 넣음
    • l 배열 : 자기자신 인덱스, 다음값부터 자식 인덱스
    • r 배열 : 자식 마지막 인덱스
    • 업데이트 시 l+1 부터 r까지 업데이트

시간복잡도 계산

  • 세그먼트 트리 생성 : O(NlgN)
  • 출력 : O(MlgN)

인트 계산

  • 트리 : 항상 2^31-1 이하이지만 중간에 넘칠 수 있으므로 > ll 사용
  • lazy :중간에 넘칠수 있으므로 ll 사용

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <iostream>
#include <vector>
#include <cmath>
 
using namespace std;
typedef long long ll;
 
vector<ll> tree;
vector<ll> lazy;
 
int initSalary[500010];
int l[500010];
int r[500010];
int segToOrigin[500010];
vector<int> child[500010];
 
void dfs(int cur, int &idx) {
    l[cur] = ++idx;
    for(int x : child[cur]) dfs(x, idx);
    r[cur] = idx;
}
 
void make(int node, int st, int en) {
    if(st== en) {
        tree[node] = initSalary[segToOrigin[st]];
        return;
    }
    
    int mid = (st+en)/2;
    make(node*2, st, mid);
    make(node*2+1, mid+1, en);
}
 
void lazy_update(int node, int st, int en) {
    if(lazy[node] != 0) {
        if(st == en) tree[node] += lazy[node];
        else {
            lazy[node*2+= lazy[node];
            lazy[node*2+1+= lazy[node];
        }
        lazy[node] = 0;
    }
}
 
void update(int node, int st, int en, int li, int ri, int diff) {
    lazy_update(node, st, en);
    if(ri < st || en < li) return;
    if(li <= st && en <= ri) {
        if(st == en) tree[node] += diff;
        else {
            lazy[node*2+= diff;
            lazy[node*2+1+= diff;
        }
        return;
    }
    
    int mid = (st+en)/2;
    update(node*2, st, mid, li, ri, diff);
    update(node*2+1, mid+1, en, li, ri, diff);
}
 
ll query(int node, int st, int en, int idx) {
    lazy_update(node, st, en);
    if(st == en) return tree[node];
    
    int mid = (st+en)/2;
    if(idx <= mid) return query(node*2, st, mid, idx);
    else return query(node*2+1, mid+1, en, idx);
}
 
 
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    fill(initSalary, initSalary+5000100);
    fill(l, l+5000100);
    fill(r, r+5000100);
    fill(segToOrigin, segToOrigin+5000100);
    int N,M; cin >> N >> M;
    
    cin >> initSalary[1];
    
    for(int i=2; i<=N; i++) {
        int a,b; cin >> a >> b;
        initSalary[i] = a;
        child[b].push_back(i);
    }
    int idx = -1;
    // 세그먼트 위치 설정
    dfs(1, idx);
    
    // 세그먼트에서 원래로 가는 값
    for(int i=1; i<=N; i++) {
        segToOrigin[l[i]] = i;
    }
        
    int h = ceil(log2(N));
    tree.resize(1<<(h+1));
    lazy.resize(1<<(h+1));
    
    make(1,0,N-1);
    for(int i=0; i<M; i++) {
        char order; cin >> order;
        if(order == 'p') {
            int a,b; cin >> a >> b;
            // 자식이 없으면 건너뜀
            if(l[a] == r[a]) continue;
            update(1,0,N-1,l[a]+1, r[a], b);
        } else {
            int a; cin >> a;
            cout << query(1,0,N-1,l[a]) << '\n';
        }
    }
    
    return 0;
}
 
cs

문제유형

  • 세그먼트 - 부모 자식 문제
    • 트리가 편향되어있을때 O(NM)을 세그먼트에 넣어 O(NlgM)으로 만듬
    • 세그먼트에 넣도록 노드를 배열에 각각 넣음
    • l 배열 : 자기자신 인덱스, 다음값부터 자식 인덱스
    • r 배열 : 자식 마지막 인덱스
    • 업데이트 시 l+1 부터 r까지 업데이트

문제링크 : https://www.acmicpc.net/problem/6549


아이디어

  • 시작점 * 끝점 모든 경우의 수 구할 떄
    • O(N^2) = 1e10 시간초과
  • 분할정복을 사용
    • 최소값의 양쪽의 사각형의 넓이 중 큰값을 선택
  • 세그먼트 트리로 구현
    • 세그먼트 트리 구성 : 각 노드는 가장 작은값을 가지는 인덱스를 지정
    • 구간에서 가장 작은 값의 인덱스를 찾아서
    • 그 양쪽의 값을 계산해줌

시간복잡도 계산

  • 세그먼트 트리 생성 : O(NlgN)
  • 검색 및 최대값 계산 : O(lgN)
  • 총합 : O(NlgN)

인트 계산

  • 세그먼트 값 : 최소 인덱스 : 최대 1e5, INT 가능
  • 각 히스토그램 크기 : 최대 1e9, INT 가능
  • 사각형 계산 : 최대 1e9 * 1e5 : INT 초과

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include <vector>
#include <cmath>
 
using namespace std;
typedef long long ll;
 
int nums[100010];
vector<int> tree;
int N;
 
int make(int node, int st, int en) {
    if(st==en) return tree[node] = st;
    
    int mid = (st+en)/2;
    int li = make(node*2, st, mid);
    int ri = make(node*2+1, mid+1, en);
    
    // 값 비교후 작은 인덱스 표시
    if( nums[li] <= nums[ri]) return tree[node] = li;
    else return tree[node] = ri;
}
 
int find(int node, int st, int en, int li, int ri) {
    if(ri < st || en < li) return -1;
    if(li <= st && en <=ri) return tree[node];
    
    int mid = (st+en)/2;
    int leftIndex = find(node*2, st, mid, li, ri);
    int rightIndex = find(node*2+1, mid+1, en, li, ri);
    
    if(leftIndex == -1return rightIndex;
    else if(rightIndex == -1return leftIndex;
    
    
    if(nums[leftIndex] <= nums[rightIndex]) return leftIndex;
    else return rightIndex;
    
}
 
ll query(int st, int en) {
    if(st > en) return 0;
    
    // 최소값을 기준으로 전체, 좌측, 우측 더 작은 값
    // 전체
    int mi = find(1,0,N-1, st, en);
    ll rs = (ll)(en-st+1* nums[mi];
    rs = max(rs, query(st, mi-1));
    rs = max(rs, query(mi+1, en));
    return rs;
    
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    while(1) {
        fill(nums, nums+1000100);
        
        cin >> N;
        if(N==0break;
        
        tree.clear();
        int h = ceil(log2(N));
        tree.resize(1<<(h+1));
        
        for(int i=0; i<N; i++cin >> nums[i];
        // 세그먼트 트리 생성
        make(1,0,N-1);
        
        cout << query(0,N-1<< '\n';
        
    }
    
    
    return 0;
}
 
cs

문제유형

  • 세그먼트 - 연속된 넓이
    • 트리에 규칙에 맞는 인덱스를 넣고
    • 쿼리를 통해 시작점부터 끝점까지 값을 비교

문제링크 : https://www.acmicpc.net/problem/1865


문제 추상화

  • 음의 간선을 통해 사이클이 생기는지 확인하는 문제

아이디어

  • 밸만 포트만 사용하여 사이클이 생기는지 확인
  • 사이클 확인할경우, 출발점 지정해줄 필요 없음

시간복잡도 계산

  • 테스트캐이스 : O(TC)
  • 밸만 포드만 : O(NM)
  • 총합 : O(TC * N * M) : 5 * 500 * 2500 = 6.25e6 > 가능

인트 계산

  • N * M * T의 최대 수 가능
  • 500 * 2500 * 1e4 = 1.25e6 * e4 = e10 > long long 사용

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <tuple>
#include <vector>
 
 
using namespace std;
typedef long long ll;
typedef tuple<intintint> ti3;
 
const ll inf = 2e10;
ll dist[510];
vector<ti3> edge;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    int TC; cin >> TC;
    while(TC--) {
        fill(dist, dist+5100);
        
        edge.clear();
        int N,M,W; cin >> N >> M >> W;
        for(int i=0; i<M; i++) {
            int a,b,c; cin >> a >> b >> c;
            edge.push_back({a,b,c});
            edge.push_back({b,a,c});
        }
        
        for(int i=0; i<W; i++) {
            int a,b,c; cin >> a >> b >> c;
            edge.push_back({a,b,-c});
        }
        
        // 모든 간선을 거침
        for(int i=0; i<N-1; i++) {
            for(int j=0; j<edge.size(); j++) {
                int a,b,c; tie(a,b,c) = edge[j];
                
                if(dist[b] > dist[a] + c) dist[b] = dist[a] + c;
            }
        }
        
        bool sw = false;
        for(int j=0; j<edge.size(); j++) {
            int a,b,c; tie(a,b,c) = edge[j];
            
            if(dist[b] > dist[a] + c ) {
                cout << "YES\n";
                sw = 1;
                break;
            }
        }
        
        if(sw == falsecout << "NO\n";
        
    }
    
    return 0;
}
 
cs

문제유형

  • 밸만포드 - 사이클 확인
    • 출발점 없이 사이클만 확인
    • dist 배열 시작점을 0으로 만들고 나머지 inf로 만들고 진행할 경우, 시작점 고립되어있을경우 사이클 확인 불가

+ Recent posts