미소녀 프사를 달자.


gpt를 10번은 혼내서 얻어따.. 진지하게 깃허브 프사까지 바꾸려다가 참음
미소녀 프사를 달자.
gpt를 10번은 혼내서 얻어따.. 진지하게 깃허브 프사까지 바꾸려다가 참음
https://devyeonee911.tistory.com/2
Presigned URL로 S3 프라이빗 버킷 이미지 조회하기 (SDK v2)
프로메사 프로젝트를 진행하던 중, 원래 계획에는 없었던 Presigned URL을 도입하게 되었습니다. 이 글에서는 해당 기술을 왜 선택하게 되었는지 먼저 설명하고 이어서 Presigned URL의 개념을 간단히
devyeonee911.tistory.com
글을 썼다. 나름 잘 쓴 글 같기도.. 근데 시간이 많이 든다 ㅠ
개발하는 것보다 어떻게 글 쓰는 시간이 더 오래 걸린다.... 개발하는 거를 다 쓸 필요는 없지만 그래도 하나씩 얻어가는 거는 꼭 있어서 웬만해서는 기록해두고 싶은데 그 속도를 못 따라간다.
[내 얘기] 의욕 없을 때는 달콤쌉싸름 자몽차🍊 (5) | 2025.04.13 |
---|---|
[내 얘기] 플래티넘 달성 :: 🪸푸른 산호초🪸 (7) | 2025.04.06 |
[내 얘기] 밤에는 노란 간접 조명을 켜고 노래를 틀어요 (0) | 2025.03.11 |
[테트리스] 4-WIDE 연습 중... (1) | 2025.02.20 |
인생 목표 (0) | 2025.02.11 |
세오스 5주차 과제로 도커 공부 중...그냥 뭔가 데이터 통신 생각남..
데이터 통신을 생각하니 마음이 좋지 않아요
aws 시험 응시료 20만원도 날 너무 힘들게 함... 말이 안 됨
챗gpt 유료버전을 결제했다! 완전 똑똑이야
Kotlin에서 컬렉션 타입(collection type)은 여러 개의 데이터를 표현하는 방법이며 Array, List, Set, Map이 있다. List, Set, Map은 Collection 인터페이스를 구현한 클래스이며, 이를 통틀어 컬렉션 타입 클래스라고 한다. 그중 List는 순서가 있는 데이터 집합으로 데이터의 중복을 허용한다.
Collection 타입의 클래스는 가변 클래스와 불변 클래스로 나뉜다. Kotlin에서는 불변 타입인 List, 가변 타입인 MutableList를 제공한다. 불변 타입은 요소 추가, 삭제가 불가능하며, 가변 타입은 .add(), .remove() 등의 메서드를 통해 값을 자유롭게 수정할 수 있다.
listOf()함수로 List 객체를, mutableListOf()함수를 통해 MutableList를 생성할 수 있다. 또한 mutableListOf() 함수는 타입 추론이 가능하기 때문에 명시적인 타입 선언 없이도 앞서 사용한 타입을 그대로 사용할 수 있다.
fun main() {
// Immutable list
// val shoppingList = listOf("Processor", "RAM", "Graphics Card", "SSD")
// mutable list
val shoppingList = mutableListOf("Processor", "RAM", "Graphics Card RTX 3060", "SSD")
// adding items to lists
shoppingList.add("Cooling System")
shoppingList.remove("Graphics Card RTX 3060")
shoppingList.add("Graphics Card RTX 4090")
println(shoppingList) // [Processor, RAM, SSD, Cooling System, Graphics Card RTX 4090]
}
Array는 고정 크기의 배열, List는 요소 개수 제한 없는 컬렉션이다.
val은 참조(reference)를 바꿀 수 없다는 뜻으로, 그 안에 있는 내용(data)는 바꿀 수 있다.
val list = mutableListOf("a", "b", "c")
list.add("d") // ✅ 가능
list.remove("b") // ✅ 가능
// list = mutableListOf("x", "y") // ❌ 안 됨! -> 참조 자체를 바꾸려 하니까
val list로 선언한 것은 이 변수가 특정 리스트를 가리키는 참조(주소)를 바꾸지 않겠다는 의미다. 이 리스트의 주소는 고정되지만, 리스트 내부의 요소는 변경할 수 있다. 반면 var로 선언하면, 이 변수는 나중에 다른 리스트를 가리키는 것으로 변경될 수 있다. 즉, 리스트 전체를 새로 덮어쓸 수 있다는 의미다.
따라서 일반적으로는 리스트 안의 요소만 바꾸고 참조는 유지하는 경우가 많기 때문에, 리스트 전체를 다른 것으로 바꾸는 실수를 막기 위해 val 선언을 권장한다.
set() 메서드는 리스트에서 특정 인덱스의 항목을 새로운 값으로 교체할 때 사용된다. 두 개의 매개변수를 받으며, 첫 번째는 바꾸고 싶은 요소의 인덱스, 두 번째는 새로 넣을 값이다.
contains() 메서드는 리스트 안에 특정 항목이 포함되어 있는지 여부를 확인할 때 사용한다. 찾고 싶은 값을 인자로 전달하면 되고, 결과는 Boolean 타입으로 반환된다.
package com.example.kotlinbasics
fun main() {
val fruitsList = mutableListOf("apple", "peach", "strawberry", "cherry", "banana")
println(fruitsList)
fruitsList.add("melon")
println(fruitsList)
fruitsList.remove("apple")
println(fruitsList)
if (fruitsList.contains("watermelon")) {
println("Has watermelon")
}
else {
println("No watermelon")
}
}
for(item in shoppingList) {
println(item)
if (item == "RAM"){
shoppingList.removeLast()
break;
}
}
println(shoppingList)
[Android 14 및 Kotlin 개발 완전 정복하기] 3일차 - 함수, 객체 및 커피 머신 (4) | 2025.04.12 |
---|---|
[Android 14 및 Kotlin 개발 완전 정복하기] 2일차 - 가위 바위 보 및 코틀린 기초 (1) | 2025.04.12 |
[Android 14 및 Kotlin 개발 완전 정복하기] 1일차 - 모든 것 설정하기 (1) | 2025.04.07 |
으l욕 없을 ㄸĦ는 달콤쌉ᄊト름 ㅈΓ몽ㅊr를 ㅁトんıヱ ㅎb을 Łй보ㅈΓ
그냥 머리 아픈 거 가타요... 호엥...
[내 얘기] 글 쓰는 건 귀차낭 (2) | 2025.06.15 |
---|---|
[내 얘기] 플래티넘 달성 :: 🪸푸른 산호초🪸 (7) | 2025.04.06 |
[내 얘기] 밤에는 노란 간접 조명을 켜고 노래를 틀어요 (0) | 2025.03.11 |
[테트리스] 4-WIDE 연습 중... (1) | 2025.02.20 |
인생 목표 (0) | 2025.02.11 |
Although the singer gave a competent performance, audience members were disappointed because they were expecting a superior one.
The auto maker's first compact car quickly sold in the millions upon its release, and this was largely due to its affordability.
Beth Tolley was selected to supervise the Snapfire project, as she understands the details of the undertaking better than most.
The company receives correspondence from customers on a regular basis, most of whom request more information about products or inquire about product use.
커피 없이는 코드 한 줄도 못짜는 Amy(와 본인)
코틀린에서 함수를 선언하려면 fun이라는 키워드를 이용한다.
fun makeCoffee(name : String, sugarCount : Int) {
if (sugarCount == 0) {
println("Coffee with no sugar for $name")
}
else if (sugarCount == 1) {
println("Coffee with $sugarCount spoon of sugar for $name")
}
else {
println("Coffee with $sugarCount spoons of sugar for $name")
}
}
함수의 매개변수에는 기본값을 선언할 수 있다. 만약 어떤 매개변수에 기본값을 선언했다면 호출할 때 인자를 전달하지 않아도 되며 이때 선언문에 명시한 기본값이 적용된다.
fun read(
b: ByteArray,
off: Int = 0,
len: Int = b.size,
) { /*...*/ }
package com.example.kotlinbasics
fun main() {
println("Who is this coffee for?")
val name = readln()
println("How many pieces of sugar do you want?")
val sugarCount = readln().toInt()
makeCoffee(name, sugarCount)
}
// Define Function
fun makeCoffee(name : String, sugarCount : Int) {
if (sugarCount == 0) {
println("Coffee with no sugar for $name")
}
else if (sugarCount == 1) {
println("Coffee with $sugarCount spoon of sugar for $name")
}
else {
println("Coffee with $sugarCount spoons of sugar for $name")
}
}
fun divide(num1 : Int, num2 : Int):Double {
val result = num1 / num2
return result
}
해당 함수는 에러가 발생한다. Int와 Int를 나누면 결과가 Int일 것이라고 예상하지만, 반환 타입을 Double로 명시해두었기 때문에 오류가 발생하는 것이다.
이 문제를 해결하려면 num1과 num2 모두를 Double로 변환하거나, 둘 중 하나만 Double로 변환해도 된다.
코틀린에서 클래스는 class 키워드로 선언한다. 이때
클래스 이름은 무조건 단수로 짓는다.
클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성된다. 이 중 생성자는 constructor 키워드로 선언하는 함수이다. 그리고 클래스 안에 다른 클래스를 선언할 수도 있다. 이때 생성자는 주 생성자와 보조 생성자로 구분되는데 주 생성자는 클래스 선언부에 constructor 키워드를 사용해 정의하며 반드시 선언해야 하는 것은 아니고 한 클래스에 하나만 가질 수 있다. 만약 어노테이션이나 public, private과 같은 접근 제한자가 없다면, constructor 키워드는 생략할 수 있다.
init 키워드로 지정한 영역은 클래스 객체를 생성하는 순간 자동으로 실행된다.
class User(name: String, count: Int) {
init {
println("I am init...")
}
}
fun main() {
val user = User("yeonee", 10)
}
// i am init...
생성자의 매개변수는 기본적으로 생성자에서만 사용할 수 있는 지역 변수이다. 즉 init 영역에서만 사용가능하다.
class User(name: String, count: Int) {
init {
println("name : $name, count : $count") // 성공
}
fun someFun() {
println("name : $name, count : $count") // 오류
}
]
코틀린은 프로퍼티를 선언하고, 그것을 주 생성자에서 초기화하는 간결한 문법을 제공한다. 주 생성자 안에서 val이나 var 키워드로 선언하면 클래스의 멤버 변수가 된다.
멤버 변수, 즉 프로퍼티는 실제 객체의 일부라는 뜻이고 매개변수는 그저 객체에 정보를 전달한 것이다. 변수나 클래스의 프로퍼티를 String으로 다루고 싶다면 중괄호를 추가해서 어디서부터 어디까지가 영역인지 구분한다. 또한 프로퍼티에도 기본값을 줄 수 있다.
package com.example.kotlinbasics
class Book (val title : String = "Unknown",
val author : String = "Anonymous",
val yearPublished : Int = 2025) {
}
package com.example.kotlinbasics
fun main() {
val myBook = Book()
println("title : ${myBook.title}, " +
"author : ${myBook.author}, " +
"yearPublished : ${myBook.yearPublished}")
}
코틀린의 데이터 클래스는 주로 데이터를 저장하는 데 사용된다. class대신에 data class 키워드를 사용하여 선언하다.data class를 사용하면 toString(), equals(), hashCode() 같은 유용한 함수들이 자동으로 만들어진다. 데이터 클래스에서는 별도의 함수 정의가 불가능하다.
데이터 클래스의 주요 특징은 다음과 같다.
우선, 데이터 클래스는 주로 val을 사용해 불변 속성을 권장하므로, 단순하고 변경되지 않는 데이터를 표현하기에 적합하다. 또한, toString(), equals(), hashCode() 같은 기본 메서드들을 코틀린이 자동으로 생성해준다. 마지막으로, 구조 분해 선언을 지원해 객체의 속성들을 개별 변수로 간편하게 꺼내 쓸 수 있다.
package com.example.kotlinbasics
data class CoffeeDetails(
val sugarCount: Int,
val name: String,
val size: String,
val creamAmount: Int)
fun main() {
val coffeeForYeonee = CoffeeDetails(0, "yeonee", "L", 0)
makeCoffee(coffeeForYeonee)
}
// Define Function
fun makeCoffee(coffeeDetails: CoffeeDetails) {
if (coffeeDetails.sugarCount == 0) {
println("Coffee with no sugar for ${coffeeDetails.name}")
}
else if (coffeeDetails.sugarCount == 1) {
println("Coffee with ${coffeeDetails.sugarCount} " +
"spoon of sugar for ${coffeeDetails.name}")
}
else {
println("Coffee with ${coffeeDetails.sugarCount} spoons of sugar for ${coffeeDetails.name}")
}
}
[Android 14 및 Kotlin 개발 완전 정복하기] 4일차-리스트와 객체 (0) | 2025.04.16 |
---|---|
[Android 14 및 Kotlin 개발 완전 정복하기] 2일차 - 가위 바위 보 및 코틀린 기초 (1) | 2025.04.12 |
[Android 14 및 Kotlin 개발 완전 정복하기] 1일차 - 모든 것 설정하기 (1) | 2025.04.07 |
ETT로도 풀린다는데 모르겠어서 HLD로 풀었습니다. 일단 이 문제는 영어라서 해석부터 해보자면...
마찬가지로 간선에 값이 있는 경우이기 때문에 부모와 자식 노드 중 자식 노드에 정보를 저장했습니다. 그러면 포인트 업데이트가 되고, 시골도로를 1, 고속도로를 0으로 두어 구간합 세그먼트 트리를 구성했습니다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n; cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 1; i < n; i++) {
int a, b; cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
vector<int>dep(n + 1, 0);
vector<int>par(n + 1, -1);
vector<int>sz(n + 1, 1);
auto dfs1 = [&](const auto& self, int cur)-> void {
for (int& nxt : g[cur]){
auto it = find(g[nxt].begin(), g[nxt].end(), cur);
g[nxt].erase(it);
dep[nxt] = dep[cur] + 1;
par[nxt] = cur;
self(self, nxt);
sz[cur] += sz[nxt];
if (sz[g[cur][0]] < sz[nxt]) swap(g[cur][0], nxt);
}
};
dfs1(dfs1, 1);
vector<int>in(n + 1, 0);
vector<int>top(n + 1, 0);
int cnt = 0;
auto dfs2 = [&](const auto& self, int cur) -> void {
in[cur] = ++cnt;
for (int nxt : g[cur]){
if (nxt == g[cur][0]) top[nxt] = top[cur];
else top[nxt] = nxt;
self(self, nxt);
}
};
top[1] = 1;
dfs2(dfs2, 1);
int t_sz = 1;
while(t_sz < n) t_sz *= 2;
vector<int> tree(t_sz * 2, 0);
auto update = [&](int i) { // i is children
i = i - 1 + t_sz;
if (tree[i] == 0) tree[i] = 1;
else tree[i] = 0;
while(i > 1) {
i /= 2;
tree[i] = tree[i * 2] + tree[i * 2 + 1];
}
};
auto query = [&](int l, int r) {
l = l - 1 + t_sz;
r = r - 1 + t_sz;
int res = 0;
while(l <= r) {
if (l % 2 == 1) res += tree[l], l++;
if (r % 2 == 0) res += tree[r], r--;
l /= 2, r /= 2;
}
return res;
};
// init
for (int i = 2; i <= n;i++) update(i);
auto edge_update = [&](int a, int b) {
// b is children
if (par[a] == b) swap(a, b);
update(in[b]);
};
auto path_query = [&](int a, int b) {
int res = 0;
while(top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
res += query(in[top[a]], in[a]);
a = par[top[a]];
}
if (dep[a] > dep[b]) swap(a, b);
res += query(in[a] + 1, in[b]);
return res;
};
int m; cin >> m;
for (int i = 1; i < m + n; i++) {
string str; cin >> str;
int a; cin >> a;
if (str == "A") {
int b; cin >> b;
edge_update(a, b);
}
else {
cout << path_query(1, a) << '\n';
}
}
}
플4까지 단 90..!
문제 풀 때마다 기여 중인데 은근 레이팅이 잘 오릅니다. 2-3씩 오르는
[BOJ 5916 | C++] 농장 관리 :: HLD, Lazy Propagation (2) | 2025.04.11 |
---|---|
[BOJ 13512 | C++] 트리와 쿼리 3 :: HLD (0) | 2025.04.11 |
[BOJ 14287 | C++] 회사 문화 3 :: ETT(오일러 경로 테크닉) (2) | 2025.03.28 |
[BOJ | C++] N과 M 시리즈 :: 백트래킹 (5) | 2025.03.25 |
[BOJ 11438 | C++] LCA 2 :: LCA (2) | 2025.02.20 |
2일차! 부족한 개념은 Do it! 책을 활용했습니당. 코틀린 공식 문서가 굉장히 잘 되어있네요... 그치만 영어는 시러
몇 가지 부분은 건너뛰었습니당
Type | Size (bits) | Min value | Max value |
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
Long | 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
Kotlin에서는 변수를 선언하는 두 가지 방법으로 val과 var이 있다.
변수명 뒤에는 콜론(:)을 추가해 타입을 명시할 수 있으며, 타입을 명시하지 않아도 자동으로 Int로 저장된다. 이를 타입 추론이라고 한다. 만약 Int 범위가 넘어간다면 자동으로 Long으로 저장된다.
큰 숫자를 사용할 예정으로 Long을 쓰고 싶다면 L을 표기한다. 만약 Byte 또는 Short를 사용하고 싶다면 콜론 기호(:)를 통해 명시적으로 선언한다.
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1
온라인에서 코틀린을 실행시킬 수 있는 사이트
Type | Size (bits) | Min value | Max value |
UByte | 8 | 0 | 255 |
UShort | 16 | 0 | 65,535 |
UInt | 32 | 0 | 4,294,967,295 (232 - 1) |
ULong | 64 | 0 | 18,446,744,073,709,551,615 (264 - 1) |
-128 ~ 127 대신 0~255를 사용한다. 1이 아닌 0부터 시작하고 양수 방향으로만 범위가 커진다.
대입하는 값 끝에 u또는 U를 붙여줘야 한다. 대문자, 소문자는 관계없다.
val b: UByte = 1u // UByte, expected type provided
val s: UShort = 1u // UShort, expected type provided
val l: ULong = 1u // ULong, expected type provided
val a1 = 42u // UInt: no expected type provided, constant fits in UInt
val a2 = 0xFFFF_FFFF_FFFFu // ULong: no expected type provided, constant doesn't fit in UInt
val a = 1UL // ULong, even though no expected type provided and the constant fits into UInt
정수의 전체 bit범위를 양수로 사용하고 싶을 때 부호없는 정수를 사용한다.
오직 두가지 값 true 또는 false를 가진다. Boolean?은 true, false, null 세 가지 값 중 하나를 가질 수 있다.
또한 세 개의 내장된 연산자를 가진다.
하나의 문자만 저장 가능하며 작은 따옴표(')로 감싸서 표현한다.
유니코드를 표현하려면, \u를 붙이고 뒤에 유니코드 숫자만 붙인다.
println('\uFF00')
일련의 문자, 즉 단어나 문단들 같은 긴 문자 같은 것들을 저장할 수 있다. 큰따옴표(")로 감싸서 표현한다. 문자열(String)의 요소들은 문자(Char)들이며, s[i]와 같은 인덱싱 연산을 통해 접근할 수 있다. 또한, for (c in str) 같은 for문을 사용해 이 문자들을 순회(iterate)할 수 있다.
한 번 문자열을 초기화하면, 그 값을 바꾸거나 새 값을 할당할 수 없다. 문자열을 변형하는 모든 연산은 새로운 String 객체로 결과를 반환하며, 원래 문자열은 그대로 유지된다.
val str = "abcd"
// Creates and prints a new String object
println(str.uppercase())
// ABCD
// The original string remains the same
println(str)
// abcd
readln() 함수를 사용하여 표준 입력으로부터 데이터를 읽는다. 이때 전체 줄을 문자열로 읽는다. 만약 다른 자료형으로 변환하고 싶으면 .toInt(), .toLong(), .toDouble(), .toFloat(), 또는 .toBoolean()와 같은 함수를 이용한다. 즉 .toInt()를 사용해서 Integer을 만들 수 있다. 이는 String을 파싱하여 Integer 숫자로 결과를 반환한다.
val age = readln().toInt()
.toInt(), .toDouble() 같은 형 변환 함수는, 입력된 문자열이 해당 자료형에 맞는 올바른 값일 거라고 가정하고 동작한다. 따라서서 "hello"처럼 숫자가 아닌 문자열을 변환하려고 하면 에러(예외)가 발생한다.
if (age in 18..39){...}
if (age >= 18 && age < 40){...}
in은 범위 지정 연산자로, 두 조건식은 같은 의미이다.
package com.example.rockpaperscissors
fun main(){
var computerChoice = ""
var playerChoice = ""
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln()
val randomNumber = (1..3).random()
if (randomNumber == 1) computerChoice = "Rock"
else if (randomNumber == 2) computerChoice = "Paper"
else computerChoice = "Scissors"
println(computerChoice)
}
1부터 3까지의 숫자 중 .random() 함수를 이용하여 숫자 하나를 무작위로 선정하여 변수 randomNumber에 저장한다.
when문을 이용한다. when 키워드 다음의 소괄호()에 넣은 데이터가 조건이 되고 이 값에 따라 -> 오른쪽에 있는 구문을 실행한다. 또는 데이터를 명시하지 않고 조건만 명시할 수도 있다. 표현식으로도 사용할 수 있는데, 즉 when 문의 실행 결과를 반환할 수 있다. when문을 표현식으로 사용할 때는 else문을 생략할 수 없다.
val winner = when {
playerChoice == computerChoice -> "Tie"
playerChoice == "Rock" && computerChoice == "Scissors" -> "Player"
playerChoice == "Paper" && computerChoice == "Rock" -> "Player"
playerChoice == "Scissors" && computerChoice == "Paper" -> "Player"
else -> "Computer"
}
if (winner == "Tie") {
println("It's a tie")
}
else {
println(winner + " won!")
}
if (winner == "Tie") {
println("It's a tie")
}
else {
println("$winner won!")
}
String 타입의 데이터에 변숫값이나 어떤 연산식의 결괏값을 포함해야 할 때는 $ 기호를 이용한다. 이를 문자열 템플릿이라고 한다.
fun main() {
var count = 0
while (count < 3) {
println("Count is $count")
count++
}
}
fun main() {
var userInput = readln()
while (userInput == "1") {
println("While loop executed")
userInput = readln()
}
println("Loop is done!")
}
package com.example.rockpaperscissors
fun main(){
var computerChoice = ""
var playerChoice = ""
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln().lowercase()
while(playerChoice != "rock" && playerChoice != "paper" && playerChoice != "scissors"){
println("Rock, Paper or Scissors? Enter your choice!")
playerChoice = readln().lowercase()
}
val randomNumber = (1..3).random()
when (randomNumber) {
1 -> {
computerChoice = "Rock"
}
2 -> {
computerChoice = "Paper"
}
3 -> {
computerChoice = "Scissors"
}
}
println(computerChoice)
computerChoice = computerChoice.lowercase()
val winner = when {
playerChoice == computerChoice -> "Tie"
playerChoice == "rock" && computerChoice == "scissors" -> "Player"
playerChoice == "paper" && computerChoice == "rock" -> "Player"
playerChoice == "scissors" && computerChoice == "paper" -> "Player"
else -> "Computer"
}
if (winner == "Tie") {
println("It's a tie")
}
else {
println("$winner won!")
}
}
println(computerChoice) // Rock
computerChoice.lowercase()
println(computerChoice) // Rock
computerChoice = computerChoice.lowercase()
println(computerChoice) // rock
https://tutorials.eu/exploring-basic-kotlin-syntax-and-structure-day-2-android-14-masterclass/
Exploring Basic Kotlin Syntax - Day 2 Android 14 Masterclass
Welcome to Day 2 of our Android 14 Masterclass, where we dive into the Basic Kotlin Syntax and Structure. Embark on this coding journey!
tutorials.eu
[Android 14 및 Kotlin 개발 완전 정복하기] 4일차-리스트와 객체 (0) | 2025.04.16 |
---|---|
[Android 14 및 Kotlin 개발 완전 정복하기] 3일차 - 함수, 객체 및 커피 머신 (4) | 2025.04.12 |
[Android 14 및 Kotlin 개발 완전 정복하기] 1일차 - 모든 것 설정하기 (1) | 2025.04.07 |
update와 query 모두 경로이며 특히 update는 range update이다. 따라서 lazy가 사용되어야함을 알 수 있습니다.
처음에는 hld에 어떻게 lazy 세그트리를 적용하지? 고민이 되어서 lazy가 아니라 그냥 세그먼트 트리로도 풀리나?라고 생각을 했었습니다. 그러나 아무리 봐도 이건 range update로 lazy를 쓰는 게 확실해 보이는데.......
결론은, u에서 v로 가는 path별로 lazy propagation을 이용해 업데이트합니다. 그리고 query 출력에서도 각 path별로 값을 구해서 전부 합한 값을 출력합니다.
그리고 도로, 즉 간선에 나무를 심습니다. 이때 부모와 자식 중 자식 노드에 값을 저장합니다. 이 아이디어는 트리와 쿼리 1 (https://www.acmicpc.net/problem/13510)에서 얻었습니다. 이처럼 자식 노드에 값을 저장하기 때문에 u와 v의 top이 같아졌을 때, update 및 query를 호출할 경우 첫번째 인자로 in[u] + 1, 1을 더해줘야합니다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n, m; cin >> n >> m;
vector<vector<int>> g(n + 1);
for (int i = 1; i < n;i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
// path decomposition
vector<int> dep(n + 1, 0);
vector<int> par(n + 1, -1);
vector<int> sz(n + 1, 1);
auto dfs1 = [&](const auto& self, int cur) -> void {
for (auto& nxt : g[cur]) {
auto it = find(g[nxt].begin(), g[nxt].end(), cur);
g[nxt].erase(it);
dep[nxt] = dep[cur] + 1;
par[nxt] = cur;
self(self, nxt);
sz[nxt] += sz[cur];
if (sz[g[cur][0]] < sz[nxt]) swap(g[cur][0], nxt);
}
};
dfs1(dfs1, 1);
vector<int> in(n + 1, 0);
vector<int> top(n + 1, 0);
int cnt = 0;
auto dfs2 = [&](const auto& self, int cur)-> void {
in[cur] = ++cnt;
for (int nxt : g[cur]) {
if (nxt == g[cur][0]) top[nxt] = top[cur];
else top[nxt] = nxt;
self(self, nxt);
}
};
top[1] = 1;
dfs2(dfs2, 1);
int t_sz = 1;
while(t_sz < n) t_sz *= 2;
vector<int> tree(t_sz * 2, 0);
vector<int> lazy(t_sz * 2, 0);
auto push = [&](int node, int l, int r) -> void {
if (lazy[node] != 0) {
tree[node] += lazy[node] * (r - l + 1);
if (l != r) {
lazy[node * 2] += lazy[node];
lazy[node * 2 + 1] += lazy[node];
}
lazy[node] = 0;
}
};
auto update = [&](int l, int r) {
auto rec = [&](const auto& self, int node, int node_l, int node_r) -> void {
push(node, node_l, node_r);
if (r < node_l || node_r < l) return;
if (l <= node_l && node_r <= r) {
lazy[node] = 1;
push(node, node_l, node_r);
return;
}
int mid = (node_l + node_r) / 2;
self(self, node * 2, node_l, mid);
self(self, node * 2 + 1, mid + 1, node_r);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
};
rec(rec, 1, 1, t_sz);
};
auto query = [&](int l, int r) -> long long {
auto rec = [&](const auto& self, int node, int node_l, int node_r) -> long long {
push(node, node_l, node_r);
if (r < node_l || node_r < l) return 0;
if (l <= node_l && node_r <= r) return tree[node];
int mid = (node_l + node_r) / 2;
return self(self, node * 2, node_l, mid) + self(self, node * 2 + 1, mid + 1, node_r);
};
return rec(rec, 1, 1, t_sz);
};
auto path_update = [&](int u, int v) {
while(top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
update(in[top[u]], in[u]);
u = par[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
update(in[u] + 1, in[v]);
};
auto path_query = [&](int u, int v) -> long long {
int res = 0;
while(top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v);
res += query(in[top[u]], in[u]);
u = par[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
res += query(in[u] + 1, in[v]);
return res;
};
for (int i = 0;i < m;i++) {
string q; cin >> q;
int u, v; cin >> u >> v;
if (q == "P") path_update(u, v);
else cout << path_query(u, v) << '\n';
}
}
[BOJ 8145 | C++] Megalopolis :: HLD (2) | 2025.04.12 |
---|---|
[BOJ 13512 | C++] 트리와 쿼리 3 :: HLD (0) | 2025.04.11 |
[BOJ 14287 | C++] 회사 문화 3 :: ETT(오일러 경로 테크닉) (2) | 2025.03.28 |
[BOJ | C++] N과 M 시리즈 :: 백트래킹 (5) | 2025.03.25 |
[BOJ 11438 | C++] LCA 2 :: LCA (2) | 2025.02.20 |