diff --git a/gradle.properties b/gradle.properties
index d0ccec97..732fad51 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,2 @@
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
-org.gradle.jvmargs=-Xms256m -Xmx1024m
+org.gradle.jvmargs=-Xms512m -Xmx2048m
diff --git a/src/main/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/Solution.kt b/src/main/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/Solution.kt
new file mode 100644
index 00000000..871633ea
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/Solution.kt
@@ -0,0 +1,39 @@
+package g3501_3600.s3582_generate_tag_for_video_caption
+
+// #Easy #String #Simulation #2025_06_17_Time_3_ms_(100.00%)_Space_45.13_MB_(85.00%)
+
+class Solution {
+ fun generateTag(caption: String): String? {
+ var caption = caption
+ val sb = StringBuilder()
+ sb.append('#')
+ var space = false
+ caption = caption.trim { it <= ' ' }
+ for (i in 0..
= 'A' && c <= 'Z') {
+ if (space) {
+ space = !space
+ sb.append(c)
+ } else {
+ sb.append(c.lowercaseChar())
+ }
+ }
+ if (c >= 'a' && c <= 'z') {
+ if (space) {
+ space = !space
+ sb.append(c.uppercaseChar())
+ } else {
+ sb.append(c)
+ }
+ }
+ }
+ if (sb.length > 100) {
+ return sb.substring(0, 100)
+ }
+ return sb.toString()
+ }
+}
diff --git a/src/main/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/readme.md b/src/main/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/readme.md
new file mode 100644
index 00000000..d64fccb6
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/readme.md
@@ -0,0 +1,51 @@
+3582\. Generate Tag for Video Caption
+
+Easy
+
+You are given a string `caption` representing the caption for a video.
+
+The following actions must be performed **in order** to generate a **valid tag** for the video:
+
+1. **Combine all words** in the string into a single _camelCase string_ prefixed with `'#'`. A _camelCase string_ is one where the first letter of all words _except_ the first one is capitalized. All characters after the first character in **each** word must be lowercase.
+
+2. **Remove** all characters that are not an English letter, **except** the first `'#'`.
+
+3. **Truncate** the result to a maximum of 100 characters.
+
+
+Return the **tag** after performing the actions on `caption`.
+
+**Example 1:**
+
+**Input:** caption = "Leetcode daily streak achieved"
+
+**Output:** "#leetcodeDailyStreakAchieved"
+
+**Explanation:**
+
+The first letter for all words except `"leetcode"` should be capitalized.
+
+**Example 2:**
+
+**Input:** caption = "can I Go There"
+
+**Output:** "#canIGoThere"
+
+**Explanation:**
+
+The first letter for all words except `"can"` should be capitalized.
+
+**Example 3:**
+
+**Input:** caption = "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+
+**Output:** "#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+
+**Explanation:**
+
+Since the first word has length 101, we need to truncate the last two letters from the word.
+
+**Constraints:**
+
+* `1 <= caption.length <= 150`
+* `caption` consists only of English letters and `' '`.
\ No newline at end of file
diff --git a/src/main/kotlin/g3501_3600/s3583_count_special_triplets/Solution.kt b/src/main/kotlin/g3501_3600/s3583_count_special_triplets/Solution.kt
new file mode 100644
index 00000000..6339eb08
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3583_count_special_triplets/Solution.kt
@@ -0,0 +1,23 @@
+package g3501_3600.s3583_count_special_triplets
+
+// #Medium #Array #Hash_Table #Counting #2025_06_17_Time_238_ms_(55.56%)_Space_83.48_MB_(77.78%)
+
+class Solution {
+ fun specialTriplets(nums: IntArray): Int {
+ val mod = 1_000_000_007
+ var res = 0
+ val left = mutableMapOf()
+ val right = mutableMapOf()
+ for (num in nums) {
+ right[num] = right.getOrDefault(num, 0) + 1
+ }
+ for (num in nums) {
+ right[num] = right[num]!! - 1
+ val ci = left.getOrDefault(num * 2, 0)
+ val ck = right.getOrDefault(num * 2, 0)
+ res = ((res + 1L * ci * ck) % mod).toInt()
+ left[num] = left.getOrDefault(num, 0) + 1
+ }
+ return res
+ }
+}
diff --git a/src/main/kotlin/g3501_3600/s3583_count_special_triplets/readme.md b/src/main/kotlin/g3501_3600/s3583_count_special_triplets/readme.md
new file mode 100644
index 00000000..3f704fb0
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3583_count_special_triplets/readme.md
@@ -0,0 +1,67 @@
+3583\. Count Special Triplets
+
+Medium
+
+You are given an integer array `nums`.
+
+A **special triplet** is defined as a triplet of indices `(i, j, k)` such that:
+
+* `0 <= i < j < k < n`, where `n = nums.length`
+* `nums[i] == nums[j] * 2`
+* `nums[k] == nums[j] * 2`
+
+Return the total number of **special triplets** in the array.
+
+Since the answer may be large, return it **modulo** 109 + 7
.
+
+**Example 1:**
+
+**Input:** nums = [6,3,6]
+
+**Output:** 1
+
+**Explanation:**
+
+The only special triplet is `(i, j, k) = (0, 1, 2)`, where:
+
+* `nums[0] = 6`, `nums[1] = 3`, `nums[2] = 6`
+* `nums[0] = nums[1] * 2 = 3 * 2 = 6`
+* `nums[2] = nums[1] * 2 = 3 * 2 = 6`
+
+**Example 2:**
+
+**Input:** nums = [0,1,0,0]
+
+**Output:** 1
+
+**Explanation:**
+
+The only special triplet is `(i, j, k) = (0, 2, 3)`, where:
+
+* `nums[0] = 0`, `nums[2] = 0`, `nums[3] = 0`
+* `nums[0] = nums[2] * 2 = 0 * 2 = 0`
+* `nums[3] = nums[2] * 2 = 0 * 2 = 0`
+
+**Example 3:**
+
+**Input:** nums = [8,4,2,8,4]
+
+**Output:** 2
+
+**Explanation:**
+
+There are exactly two special triplets:
+
+* `(i, j, k) = (0, 1, 3)`
+ * `nums[0] = 8`, `nums[1] = 4`, `nums[3] = 8`
+ * `nums[0] = nums[1] * 2 = 4 * 2 = 8`
+ * `nums[3] = nums[1] * 2 = 4 * 2 = 8`
+* `(i, j, k) = (1, 2, 4)`
+ * `nums[1] = 4`, `nums[2] = 2`, `nums[4] = 4`
+ * `nums[1] = nums[2] * 2 = 2 * 2 = 4`
+ * `nums[4] = nums[2] * 2 = 2 * 2 = 4`
+
+**Constraints:**
+
+* 3 <= n == nums.length <= 105
+* 0 <= nums[i] <= 105
\ No newline at end of file
diff --git a/src/main/kotlin/g3501_3600/s3584_maximum_product_of_first_and_last_elements_of_a_subsequence/Solution.kt b/src/main/kotlin/g3501_3600/s3584_maximum_product_of_first_and_last_elements_of_a_subsequence/Solution.kt
new file mode 100644
index 00000000..ec3efa8e
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3584_maximum_product_of_first_and_last_elements_of_a_subsequence/Solution.kt
@@ -0,0 +1,20 @@
+package g3501_3600.s3584_maximum_product_of_first_and_last_elements_of_a_subsequence
+
+// #Medium #Array #Two_Pointers #2025_06_17_Time_8_ms_(100.00%)_Space_80.95_MB_(50.00%)
+
+import kotlin.math.max
+import kotlin.math.min
+
+class Solution {
+ fun maximumProduct(nums: IntArray, m: Int): Long {
+ var ma = nums[0].toLong()
+ var mi = nums[0].toLong()
+ var res = nums[0].toLong() * nums[m - 1]
+ for (i in m - 1..1 <= nums.length <= 105
+* -105 <= nums[i] <= 105
+* `1 <= m <= nums.length`
\ No newline at end of file
diff --git a/src/main/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/Solution.kt b/src/main/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/Solution.kt
new file mode 100644
index 00000000..53ec98dc
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/Solution.kt
@@ -0,0 +1,143 @@
+package g3501_3600.s3585_find_weighted_median_node_in_tree
+
+// #Hard #Array #Dynamic_Programming #Tree #Binary_Search #Depth_First_Search
+// #2025_06_17_Time_123_ms_(100.00%)_Space_184.68_MB_(100.00%)
+
+import kotlin.math.ceil
+import kotlin.math.ln
+
+class Solution {
+ private lateinit var adj: MutableList>
+ private lateinit var depth: IntArray
+ private lateinit var dist: LongArray
+ private lateinit var parent: Array
+ private var longMax = 0
+ private var nodes = 0
+
+ fun findMedian(n: Int, edges: Array, queries: Array): IntArray {
+ nodes = n
+ if (n > 1) {
+ longMax = ceil(ln(n.toDouble()) / ln(2.0)).toInt()
+ } else {
+ longMax = 1
+ }
+ adj = ArrayList()
+ for (i in 0..= halfWeight) {
+ var curr = u
+ for (p in longMax - 1 downTo 0) {
+ val nextNode = parent[p][curr]
+ if (nextNode != -1 && (dist[u] - dist[nextNode] < halfWeight)) {
+ curr = nextNode
+ }
+ }
+ parent[0][curr]
+ } else {
+ val remainingWeightFromLCA = halfWeight - (dist[u] - dist[lca])
+ var curr = v
+ for (p in longMax - 1 downTo 0) {
+ val nextNode = parent[p][curr]
+ if (nextNode != -1 && depth[nextNode] >= depth[lca] &&
+ (dist[nextNode] - dist[lca]) >= remainingWeightFromLCA
+ ) {
+ curr = nextNode
+ }
+ }
+ curr
+ }
+ }
+}
diff --git a/src/main/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/readme.md b/src/main/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/readme.md
new file mode 100644
index 00000000..69a344aa
--- /dev/null
+++ b/src/main/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/readme.md
@@ -0,0 +1,70 @@
+3585\. Find Weighted Median Node in Tree
+
+Hard
+
+You are given an integer `n` and an **undirected, weighted** tree rooted at node 0 with `n` nodes numbered from 0 to `n - 1`. This is represented by a 2D array `edges` of length `n - 1`, where edges[i] = [ui, vi, wi]
indicates an edge from node ui
to vi
with weight wi
.
+
+The **weighted median node** is defined as the **first** node `x` on the path from ui
to vi
such that the sum of edge weights from ui
to `x` is **greater than or equal to half** of the total path weight.
+
+You are given a 2D integer array `queries`. For each queries[j] = [uj, vj]
, determine the weighted median node along the path from uj
to vj
.
+
+Return an array `ans`, where `ans[j]` is the node index of the weighted median for `queries[j]`.
+
+**Example 1:**
+
+**Input:** n = 2, edges = [[0,1,7]], queries = [[1,0],[0,1]]
+
+**Output:** [0,1]
+
+**Explanation:**
+
+
+
+| Query | Path | Edge Weights | Total Path Weight | Half | Explanation | Answer |
+|------------|----------|---------------|--------------------|------|-------------------------------------------------------|--------|
+| `[1, 0]` | `1 → 0` | `[7]` | 7 | 3.5 | Sum from `1 → 0 = 7 >= 3.5`, median is node 0. | 0 |
+| `[0, 1]` | `0 → 1` | `[7]` | 7 | 3.5 | Sum from `0 → 1 = 7 >= 3.5`, median is node 1. | 1 |
+
+
+**Example 2:**
+
+**Input:** n = 3, edges = [[0,1,2],[2,0,4]], queries = [[0,1],[2,0],[1,2]]
+
+**Output:** [1,0,2]
+
+**E****xplanation:**
+
+
+
+| Query | Path | Edge Weights | Total Path Weight | Half | Explanation | Answer |
+|------------|--------------|--------------|--------------------|------|-----------------------------------------------------------------------------|--------|
+| `[0, 1]` | `0 → 1` | `[2]` | 2 | 1 | Sum from `0 → 1 = 2 >= 1`, median is node 1. | 1 |
+| `[2, 0]` | `2 → 0` | `[4]` | 4 | 2 | Sum from `2 → 0 = 4 >= 2`, median is node 0. | 0 |
+| `[1, 2]` | `1 → 0 → 2` | `[2, 4]` | 6 | 3 | Sum from `1 → 0 = 2 < 3`.
Sum from `1 → 2 = 2 + 4 = 6 >= 3`, median is node 2. | 2 |
+
+**Example 3:**
+
+**Input:** n = 5, edges = [[0,1,2],[0,2,5],[1,3,1],[2,4,3]], queries = [[3,4],[1,2]]
+
+**Output:** [2,2]
+
+**Explanation:**
+
+
+
+| Query | Path | Edge Weights | Total Path Weight | Half | Explanation | Answer |
+|------------|----------------------|------------------|--------------------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------|
+| `[3, 4]` | `3 → 1 → 0 → 2 → 4` | `[1, 2, 5, 3]` | 11 | 5.5 | Sum from `3 → 1 = 1 < 5.5`.
Sum from `3 → 0 = 1 + 2 = 3 < 5.5`.
Sum from `3 → 2 = 1 + 2 + 5 = 8 >= 5.5`, median is node 2. | 2 |
+| `[1, 2]` | `1 → 0 → 2` | `[2, 5]` | 7 | 3.5 | Sum from `1 → 0 = 2 < 3.5`.
Sum from `1 → 2 = 2 + 5 = 7 >= 3.5`, median is node 2. | 2 |
+
+**Constraints:**
+
+* 2 <= n <= 105
+* `edges.length == n - 1`
+* edges[i] == [ui, vi, wi]
+* 0 <= ui, vi < n
+* 1 <= wi <= 109
+* 1 <= queries.length <= 105
+* queries[j] == [uj, vj]
+* 0 <= uj, vj < n
+* The input is generated such that `edges` represents a valid tree.
\ No newline at end of file
diff --git a/src/test/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/SolutionTest.kt
new file mode 100644
index 00000000..011d7d32
--- /dev/null
+++ b/src/test/kotlin/g3501_3600/s3582_generate_tag_for_video_caption/SolutionTest.kt
@@ -0,0 +1,44 @@
+package g3501_3600.s3582_generate_tag_for_video_caption
+
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Test
+
+internal class SolutionTest {
+ @Test
+ fun generateTag() {
+ assertThat(
+ Solution().generateTag("Leetcode daily streak achieved"),
+ equalTo("#leetcodeDailyStreakAchieved"),
+ )
+ }
+
+ @Test
+ fun generateTag2() {
+ assertThat(
+ Solution().generateTag("can I Go There"),
+ equalTo("#canIGoThere"),
+ )
+ }
+
+ @Test
+ fun generateTag3() {
+ assertThat(
+ Solution()
+ .generateTag(
+ (
+ "hhhhhhhhhhhhhhhhhhhhh" +
+ "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" +
+ "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+ ),
+ ),
+ equalTo(
+ (
+ "#hhhhhhhhhhhhhhhhhhh" +
+ "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" +
+ "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+ ),
+ ),
+ )
+ }
+}
diff --git a/src/test/kotlin/g3501_3600/s3583_count_special_triplets/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3583_count_special_triplets/SolutionTest.kt
new file mode 100644
index 00000000..6725d571
--- /dev/null
+++ b/src/test/kotlin/g3501_3600/s3583_count_special_triplets/SolutionTest.kt
@@ -0,0 +1,28 @@
+package g3501_3600.s3583_count_special_triplets
+
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Test
+
+internal class SolutionTest {
+ @Test
+ fun specialTriplets() {
+ assertThat(Solution().specialTriplets(intArrayOf(6, 3, 6)), equalTo(1))
+ }
+
+ @Test
+ fun specialTriplets2() {
+ assertThat(
+ Solution().specialTriplets(intArrayOf(0, 1, 0, 0)),
+ equalTo(1),
+ )
+ }
+
+ @Test
+ fun specialTriplets3() {
+ assertThat(
+ Solution().specialTriplets(intArrayOf(8, 4, 2, 8, 4)),
+ equalTo(2),
+ )
+ }
+}
diff --git a/src/test/kotlin/g3501_3600/s3584_maximum_product_of_first_and_last_elements_of_a_subsequence/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3584_maximum_product_of_first_and_last_elements_of_a_subsequence/SolutionTest.kt
new file mode 100644
index 00000000..805ce4d5
--- /dev/null
+++ b/src/test/kotlin/g3501_3600/s3584_maximum_product_of_first_and_last_elements_of_a_subsequence/SolutionTest.kt
@@ -0,0 +1,31 @@
+package g3501_3600.s3584_maximum_product_of_first_and_last_elements_of_a_subsequence
+
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Test
+
+internal class SolutionTest {
+ @Test
+ fun maximumProduct() {
+ assertThat(
+ Solution().maximumProduct(intArrayOf(-1, -9, 2, 3, -2, -3, 1), 1),
+ equalTo(81L),
+ )
+ }
+
+ @Test
+ fun maximumProduct2() {
+ assertThat(
+ Solution().maximumProduct(intArrayOf(1, 3, -5, 5, 6, -4), 3),
+ equalTo(20L),
+ )
+ }
+
+ @Test
+ fun maximumProduct3() {
+ assertThat(
+ Solution().maximumProduct(intArrayOf(2, -1, 2, -6, 5, 2, -5, 7), 2),
+ equalTo(35L),
+ )
+ }
+}
diff --git a/src/test/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/SolutionTest.kt
new file mode 100644
index 00000000..bc099c61
--- /dev/null
+++ b/src/test/kotlin/g3501_3600/s3585_find_weighted_median_node_in_tree/SolutionTest.kt
@@ -0,0 +1,50 @@
+package g3501_3600.s3585_find_weighted_median_node_in_tree
+
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Test
+
+internal class SolutionTest {
+ @Test
+ fun findMedian() {
+ assertThat(
+ Solution().findMedian(
+ 2,
+ arrayOf(intArrayOf(0, 1, 7)),
+ arrayOf(intArrayOf(1, 0), intArrayOf(0, 1)),
+ ),
+ equalTo(intArrayOf(0, 1)),
+ )
+ }
+
+ @Test
+ fun findMedian2() {
+ assertThat(
+ Solution()
+ .findMedian(
+ 3,
+ arrayOf(intArrayOf(0, 1, 2), intArrayOf(2, 0, 4)),
+ arrayOf(intArrayOf(0, 1), intArrayOf(2, 0), intArrayOf(1, 2)),
+ ),
+ equalTo(intArrayOf(1, 0, 2)),
+ )
+ }
+
+ @Test
+ fun findMedian3() {
+ assertThat(
+ Solution()
+ .findMedian(
+ 5,
+ arrayOf(
+ intArrayOf(0, 1, 2),
+ intArrayOf(0, 2, 5),
+ intArrayOf(1, 3, 1),
+ intArrayOf(2, 4, 3),
+ ),
+ arrayOf(intArrayOf(3, 4), intArrayOf(1, 2)),
+ ),
+ equalTo(intArrayOf(2, 2)),
+ )
+ }
+}