[0]zero-index clytie0x/zero-index

10.6 แบบฝึกหัด

เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น

ข้อ 1 สลับที่นั่ง

ระดับง่าย เวลาไม่เกิน 1 วินาที

ห้องสอบมีที่นั่ง N ตัว หมายเลข 1 ถึง N ตอนเริ่มสอบ ที่นั่งแต่ละตัวมีนักเรียนนั่งอยู่หนึ่งคน แต่ละคนมีเลขประจำตัวของตัวเอง ระหว่างสอบ กรรมการคุมสอบสั่งย้ายที่ Q ครั้ง แต่ละครั้งบอกเลขที่นั่ง a กับ b แล้วให้นักเรียนสองคนที่นั่งอยู่ตรงนั้นสลับที่กัน ถ้า a เท่ากับ b ก็ไม่มีใครต้องย้าย ให้หาว่าเมื่อย้ายครบทุกครั้งแล้ว ที่นั่ง 1 ถึง N มีใครนั่งอยู่

ข้อนี้ให้ฝึกเขียนฟังก์ชันสลับค่าที่รับ pointer สองตัว แล้วใช้ฟังก์ชันนั้นทุกครั้งที่สลับ

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100000 และ 1 ≤ Q ≤ 100000

บรรทัดที่สองมีเลขประจำตัวของนักเรียนที่นั่ง 1 ถึง N ตามลำดับ แต่ละเลขเป็นจำนวนเต็มตั้งแต่ 1 ถึง 1000000000 และไม่ซ้ำกัน

อีก Q บรรทัดต่อมา บรรทัดละสองจำนวน a และ b โดยที่ 1 ≤ a, b ≤ N

ข้อมูลออก

บรรทัดเดียว มีเลขประจำตัวของนักเรียนที่นั่ง 1 ถึง N หลังย้ายครบ Q ครั้ง คั่นด้วยช่องว่างหนึ่งช่อง

5 3
6701 6702 6703 6704 6705
1 4
2 5
4 5
6704 6705 6703 6702 6701
3 2
31 27 45
2 2
3 1
45 27 31
ดูคำใบ้

เขียนฟังก์ชัน void swap(int *x, int *y) แบบหน้า 10.2 แล้วเรียก swap(&seat[a], &seat[b]) ที่นั่งเริ่มนับจาก 1 จะประกาศอาเรย์ N + 1 ช่องแล้วไม่ใช้ช่อง 0 ก็ได้ หรือลบ 1 ก่อนใช้ก็ได้ ลองนึกด้วยว่าถ้า a เท่ากับ b ฟังก์ชันของน้องยังได้ผลถูกไหม

ดูเฉลย
#include <stdio.h>

int seat[100001];

void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

int main()
{
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &seat[i]);
    }
    for (int i = 0; i < q; i++) {
        int a, b;
        scanf("%d %d", &a, &b);
        swap(&seat[a], &seat[b]);
    }
    for (int i = 1; i <= n; i++) {
        printf("%d", seat[i]);
        if (i < n) {
            printf(" ");
        }
    }
    printf("\n");
    return 0;
}

ข้อ 2 รายงานเลขโดด

ระดับง่าย เวลาไม่เกิน 1 วินาที

ครูคณิตศาสตร์ให้ตัวเลขมา T ตัว ทุกตัวต้องทำรายงานสี่อย่าง คือ มีกี่หลัก เลขโดดทุกหลักรวมกันได้เท่าไร เลขโดดที่น้อยที่สุด และเลขโดดที่มากที่สุด เช่น 2569 มี 4 หลัก รวมกันได้ 22 เลขโดดน้อยสุดคือ 2 มากสุดคือ 9

ข้อนี้ให้ฝึกเขียนฟังก์ชันที่ส่งผลกลับหลายค่า คือ void digit_report(long long x, int *count, int *sum, int *low, int *high) แล้วให้ main เรียกฟังก์ชันนี้หนึ่งครั้งต่อหนึ่งตัวเลข

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม T (1 ≤ T ≤ 1000) อีก T บรรทัดต่อมา บรรทัดละหนึ่งจำนวน X โดยที่ 0 ≤ X ≤ 1000000000000000000 (1018) ตัวเลขไม่มีเลข 0 นำหน้า

ข้อมูลออก

T บรรทัด บรรทัดที่ i มีจำนวนหลัก ผลรวมของเลขโดด เลขโดดที่น้อยที่สุด และเลขโดดที่มากที่สุดของตัวเลขตัวที่ i คั่นด้วยช่องว่าง

3
2569
7
1000
4 22 2 9
1 7 7 7
4 1 0 1
2
0
98089
1 0 0 0
5 34 0 9
ดูคำใบ้

แยกหลักด้วย % 10 กับ / 10 แบบหน้า 4.5 ฟังก์ชันต้องเขียนผลสี่ค่ากลับไปให้ main จึงรับเป็น pointer สี่ตัว แล้วเขียนผ่าน *count *sum *low *high ตามหน้า 10.2 ระวัง X เป็น 0 ซึ่งมีหนึ่งหลัก และ X ใหญ่ถึง 1018 ต้องเก็บใน long long

ดูเฉลย
#include <stdio.h>

void digit_report(long long x, int *count, int *sum, int *low, int *high)
{
    *count = 0;
    *sum = 0;
    *low = 9;
    *high = 0;
    do {
        int digit = x % 10;
        *count += 1;
        *sum += digit;
        if (digit < *low) {
            *low = digit;
        }
        if (digit > *high) {
            *high = digit;
        }
        x /= 10;
    } while (x > 0);
}

int main()
{
    int t;
    scanf("%d", &t);
    for (int i = 0; i < t; i++) {
        long long x;
        scanf("%lld", &x);
        int count, sum, low, high;
        digit_report(x, &count, &sum, &low, &high);
        printf("%d %d %d %d\n", count, sum, low, high);
    }
    return 0;
}

ข้อ 3 ชื่อไฟล์และนามสกุล

ระดับง่าย เวลาไม่เกิน 1 วินาที

ชมรมถ่ายภาพเก็บไฟล์ไว้ในหลายโฟลเดอร์ ที่อยู่ของไฟล์เขียนเป็น path เช่น photos/2026/trip.jpg แต่ละส่วนคั่นด้วย / ส่วนสุดท้ายคือชื่อไฟล์ ถ้าชื่อไฟล์มีจุด ส่วนที่อยู่หลังจุดตัวสุดท้ายของชื่อไฟล์คือนามสกุล ส่วนที่อยู่ก่อนจุดนั้นคือชื่อ ให้แยกชื่อกับนามสกุลของไฟล์ทุกไฟล์

ชื่อโฟลเดอร์ก็มีจุดได้ เช่น backup.old/notes ไฟล์นี้ชื่อ notes และไม่มีนามสกุล

ข้อนี้ให้ฝึกเขียนฟังก์ชัน char *find_last(char *s, char c) ที่คืน pointer ไปยังตัว c ตัวสุดท้ายในสตริง s หรือคืน NULL ถ้าไม่มี c เลย

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N (1 ≤ N ≤ 1000) อีก N บรรทัดต่อมา บรรทัดละหนึ่ง path ยาว 1 ถึง 100 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข _ - . และ / ไม่มีช่องว่าง path ไม่ขึ้นต้นและไม่ลงท้ายด้วย / ไม่มี / สองตัวติดกัน ชื่อไฟล์ไม่ขึ้นต้นและไม่ลงท้ายด้วยจุด

ข้อมูลออก

N บรรทัด บรรทัดที่ i มีชื่อของไฟล์ที่ i ตามด้วยช่องว่าง แล้วตามด้วยนามสกุล ถ้าไฟล์ไม่มีนามสกุล ให้พิมพ์ - แทนนามสกุล

4
photos/2026/trip.jpg
readme
backup.old/notes
code/week10/swap.c
trip jpg
readme -
notes -
swap c
2
music/song.final.mp3
a.b
song.final mp3
a b
ดูคำใบ้

เขียน char *find_last(char *s, char c) ที่เดินด้วย pointer ไปจนถึง '\0' แล้วจำตำแหน่งล่าสุดที่เจอ c ไว้ ถ้าไม่เจอเลยให้คืน NULL หา '/' ตัวสุดท้ายในทั้ง path ก่อน ชื่อไฟล์เริ่มที่ตัวถัดจากนั้น แล้วค่อยหา '.' ตัวสุดท้ายในชื่อไฟล์ ไม่ใช่ในทั้ง path เขียน '\0' ทับจุดนั้นก็จะตัดชื่อกับนามสกุลออกจากกันได้ทันที

ดูเฉลย
#include <stdio.h>

char *find_last(char *s, char c)
{
    char *found = NULL;
    for (char *p = s; *p != '\0'; p++) {
        if (*p == c) {
            found = p;
        }
    }
    return found;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        char path[105];
        scanf("%s", path);
        char *name = path;
        char *slash = find_last(path, '/');
        if (slash != NULL) {
            name = slash + 1;
        }
        char *dot = find_last(name, '.');
        if (dot == NULL) {
            printf("%s -\n", name);
        } else {
            *dot = '\0';
            printf("%s %s\n", name, dot + 1);
        }
    }
    return 0;
}

ข้อ 4 เก็บช่องว่าง

ระดับง่าย เวลาไม่เกิน 1 วินาที

น้องคัดลอกข้อความจากเอกสารหลายไฟล์มาต่อกัน แล้วได้ช่องว่างเกินมาเต็มไปหมด ทั้งหน้าข้อความ ท้ายข้อความ และระหว่างคำที่บางที่มีหลายช่อง ให้จัดข้อความใหม่ให้ไม่มีช่องว่างหน้าและท้ายข้อความ และระหว่างคำมีช่องว่างแค่ช่องเดียว ตัวอักษรอื่นต้องอยู่ครบตามลำดับเดิม

ข้อนี้ให้ฝึกเขียนฟังก์ชัน void tidy(char *s) ที่แก้สตริงในอาเรย์เดิม โดยไม่ใช้อาเรย์ที่สอง

ข้อมูลเข้า

ข้อความหนึ่งบรรทัด ยาว 1 ถึง 1000 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข เครื่องหมายวรรคตอน และช่องว่าง อาจมีแต่ช่องว่างทั้งบรรทัดก็ได้

ข้อมูลออก

ข้อความที่จัดแล้วหนึ่งบรรทัด ถ้าข้อความมีแต่ช่องว่าง ให้พิมพ์บรรทัดว่าง

   Hello,   my  name is    Somchai.  
Hello, my name is Somchai.
Grade:A    Score:  95
Grade:A Score: 95
ดูคำใบ้

ใช้ pointer สองตัวเดินบนสตริงเดียวกัน read เดินอ่านทุกตัว write ชี้ช่องถัดไปที่จะเขียน ตัวอักษรที่ไม่ใช่ช่องว่างคัดลอกเสมอ ส่วนช่องว่างให้คัดลอกเฉพาะเมื่อตัวที่เขียนไปล่าสุดไม่ใช่ช่องว่างและไม่ได้อยู่หัวบรรทัด write ไม่มีทางแซง read จึงเขียนทับในอาเรย์เดิมได้ จบแล้วอย่าลืมตัดช่องว่างตัวท้ายที่อาจเหลืออยู่ และเขียน '\0' ปิด

ดูเฉลย
#include <stdio.h>

char line[1005];

void tidy(char *s)
{
    char *write = s;
    for (char *read = s; *read != '\0'; read++) {
        if (*read != ' ') {
            *write = *read;
            write++;
        } else if (write > s && *(write - 1) != ' ') {
            *write = ' ';
            write++;
        }
    }
    if (write > s && *(write - 1) == ' ') {
        write--;
    }
    *write = '\0';
}

int main()
{
    fgets(line, sizeof line, stdin);
    for (char *p = line; *p != '\0'; p++) {
        if (*p == '\n' || *p == '\r') {
            *p = '\0';
        }
    }
    tidy(line);
    printf("%s\n", line);
    return 0;
}

ข้อ 5 จับฉลากของขวัญ

ระดับกลาง เวลาไม่เกิน 1 วินาที

ปีใหม่นี้โรงเรียนให้ทุกห้องจับฉลากแลกของขวัญ ห้องที่มีนักเรียน n คนใช้เลขที่ 1 ถึง n ทุกคนจับฉลากได้ชื่อเพื่อนในห้องหนึ่งคน แล้วซื้อของขวัญให้คนนั้น ฉลากแต่ละใบมีชื่อไม่ซ้ำกัน ทุกคนจึงได้รับของขวัญจากคนอื่นพอดีหนึ่งชิ้น บางคนอาจจับได้ชื่อตัวเองก็ได้ ฝ่ายกิจกรรมมีรายการว่าใครจับได้ชื่อใคร แต่อยากรู้กลับกันว่าแต่ละคนจะได้รับของขวัญจากใคร

ข้อนี้ให้ฝึกจองอาเรย์ตามขนาดของแต่ละห้องตอนรัน และคืนหน่วยความจำเมื่อใช้เสร็จ

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม T คือจำนวนห้อง (1 ≤ T ≤ 20000) แต่ละห้องมีสองบรรทัด บรรทัดแรกมีจำนวนเต็ม n (1 ≤ n ≤ 100000) บรรทัดที่สองมีจำนวนเต็ม n ตัว ตัวที่ i คือเลขที่ของเพื่อนที่นักเรียนเลขที่ i จับได้ ทุกเลขอยู่ระหว่าง 1 ถึง n และไม่ซ้ำกัน ผลรวมของ n ทุกห้องไม่เกิน 100000

ข้อมูลออก

T บรรทัด บรรทัดที่ k เป็นของห้องที่ k มีจำนวนเต็ม n ตัวคั่นด้วยช่องว่าง ตัวที่ j คือเลขที่ของคนที่ให้ของขวัญกับนักเรียนเลขที่ j

2
4
3 1 4 2
1
1
2 4 1 3
1
2
3
2 3 1
2
2 1
3 1 2
2 1
ดูคำใบ้

ถ้านักเรียน i จับได้ชื่อ g แปลว่าคนที่ให้ของขวัญกับ g คือ i จึงเขียน giver[g] = i ได้ทันทีตอนอ่าน ไม่ต้องค้นหา ขนาดของแต่ละห้องรู้ตอนรันเท่านั้น ให้จองอาเรย์ด้วย malloc ตามขนาด n + 1 ของห้องนั้น ใช้เสร็จแล้ว free ก่อนไปห้องถัดไป ตามหน้า 10.5

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int rooms;
    scanf("%d", &rooms);
    for (int r = 0; r < rooms; r++) {
        int n;
        scanf("%d", &n);
        int *giver = malloc((n + 1) * sizeof(int));
        if (giver == NULL) {
            return 1;
        }
        for (int i = 1; i <= n; i++) {
            int drawn;
            scanf("%d", &drawn);
            giver[drawn] = i;
        }
        for (int j = 1; j <= n; j++) {
            printf("%d", giver[j]);
            if (j < n) {
                printf(" ");
            }
        }
        printf("\n");
        free(giver);
    }
    return 0;
}

ข้อ 6 สลับแถวในตาราง

ระดับกลาง เวลาไม่เกิน 1 วินาที

ครูเก็บคะแนนเก็บของนักเรียนไว้ในตาราง R แถว C คอลัมน์ แถวที่ 1 ถึง R คือนักเรียนตามลำดับในสมุด คอลัมน์คือการบ้านแต่ละชิ้น ระหว่างเทอมมีนักเรียนย้ายลำดับในสมุดหลายครั้ง ครูจึงสั่ง Q ครั้ง แต่ละครั้งบอกเลขแถว a กับ b ให้แถวทั้งสองสลับกันทั้งแถว ถ้า a เท่ากับ b ตารางไม่เปลี่ยน ให้พิมพ์ตารางหลังสลับครบทุกครั้ง

ขนาดของตารางรู้ตอนรันเท่านั้น และบางตารางมีแถวน้อยแต่ยาวมาก

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม R C และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ R, C และ R × C ≤ 100000 และ 1 ≤ Q ≤ 100000

อีก R บรรทัดต่อมา บรรทัดละ C จำนวน คือคะแนนในแต่ละแถว แต่ละจำนวนอยู่ระหว่าง 0 ถึง 100

อีก Q บรรทัดต่อมา บรรทัดละสองจำนวน a และ b โดยที่ 1 ≤ a, b ≤ R

ข้อมูลออก

R บรรทัด บรรทัดละ C จำนวนคั่นด้วยช่องว่างหนึ่งช่อง คือตารางหลังสลับครบ Q ครั้ง

4 3 3
8 9 10
5 7 6
10 10 9
3 4 8
1 3
2 4
3 4
10 10 9
3 4 8
5 7 6
8 9 10
1 5 1
100 0 50 75 25
1 1
100 0 50 75 25
ดูคำใบ้

ถ้าสลับแถวด้วยการคัดลอกค่าทีละช่อง หนึ่งครั้งต้องคัดลอก C ช่อง เมื่อ C ใหญ่และ Q ใหญ่จะไม่ทัน ให้เก็บตารางแบบหน้า 10.5 คือ int **table ที่แต่ละแถวจองแยกกัน แล้วสลับแค่ pointer ของสองแถว table[a] กับ table[b] ซึ่งเป็นค่าเดียวต่อแถว ไม่ว่าแถวจะยาวเท่าไร ฟังก์ชันสลับจึงรับ int ** สองตัว

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>

void swap_rows(int **x, int **y)
{
    int *temp = *x;
    *x = *y;
    *y = temp;
}

int main()
{
    int rows, cols, q;
    scanf("%d %d %d", &rows, &cols, &q);
    int **table = malloc((rows + 1) * sizeof(int *));
    for (int r = 1; r <= rows; r++) {
        table[r] = malloc(cols * sizeof(int));
        for (int c = 0; c < cols; c++) {
            scanf("%d", &table[r][c]);
        }
    }
    for (int i = 0; i < q; i++) {
        int a, b;
        scanf("%d %d", &a, &b);
        swap_rows(&table[a], &table[b]);
    }
    for (int r = 1; r <= rows; r++) {
        for (int c = 0; c < cols; c++) {
            printf("%d", table[r][c]);
            if (c < cols - 1) {
                printf(" ");
            }
        }
        printf("\n");
    }
    for (int r = 1; r <= rows; r++) {
        free(table[r]);
    }
    free(table);
    return 0;
}

ข้อ 7 หมุนกลุ่มในแถว

ระดับยาก เวลาไม่เกิน 1 วินาที

หน้าเสาธงมีนักเรียนเข้าแถวตอนเรียงหนึ่ง N คน ตำแหน่ง 1 ถึง N แต่ละคนมีเลขประจำตัว ครูพละฝึกการเปลี่ยนแถว Q ครั้ง แต่ละครั้งครูเลือกกลุ่มที่ยืนติดกันตั้งแต่ตำแหน่ง l ถึง r แล้วสั่งว่า "คนหัวกลุ่มเดินไปต่อท้ายกลุ่ม" ซ้ำ k ครั้ง คนนอกกลุ่มอยู่ที่เดิม ให้หาว่าหลังฝึกครบ Q ครั้ง แต่ละตำแหน่งมีใครยืนอยู่

เช่น กลุ่ม 1 2 3 4 5 สั่งซ้ำ 2 ครั้งได้ 3 4 5 1 2

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม N และ Q คั่นด้วยช่องว่าง โดยที่ 1 ≤ N ≤ 100000 และ 1 ≤ Q ≤ 500

บรรทัดที่สองมีเลขประจำตัวของคนที่ตำแหน่ง 1 ถึง N แต่ละเลขเป็นจำนวนเต็มตั้งแต่ 1 ถึง 1000000

อีก Q บรรทัดต่อมา บรรทัดละสามจำนวน l r k โดยที่ 1 ≤ l ≤ r ≤ N และ 0 ≤ k ≤ 1000000000

ข้อมูลออก

บรรทัดเดียว มีเลขประจำตัวของคนที่ตำแหน่ง 1 ถึง N หลังฝึกครบ คั่นด้วยช่องว่างหนึ่งช่อง

7 2
11 12 13 14 15 16 17
2 6 2
1 7 10
16 12 13 17 11 14 15
3 2
5 4 3
1 3 0
2 2 7
5 4 3
ดูคำใบ้

หมุนครบหนึ่งรอบของกลุ่มได้แถวเดิม จึงลด k เหลือ k % len ก่อน แต่หมุนทีละคนก็ยังช้าเมื่อกลุ่มยาว ให้ใช้ฟังก์ชัน void reverse(int *first, int *last) ที่กลับลำดับตั้งแต่ first ถึง last ด้วย pointer สองตัวเดินเข้าหากันแบบหน้า 10.3 แล้วลองกลับลำดับ k คนแรกของกลุ่ม กลับลำดับคนที่เหลือ แล้วกลับลำดับทั้งกลุ่ม ลองทำด้วยมือกับ 1 2 3 4 5 และ k = 2 ดูว่าได้อะไร

ดูเฉลย
#include <stdio.h>

int line[100001];

void reverse(int *first, int *last)
{
    while (first < last) {
        int temp = *first;
        *first = *last;
        *last = temp;
        first++;
        last--;
    }
}

void rotate_left(int *first, int *last, int k)
{
    int len = last - first + 1;
    k %= len;
    if (k == 0) {
        return;
    }
    reverse(first, first + k - 1);
    reverse(first + k, last);
    reverse(first, last);
}

int main()
{
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &line[i]);
    }
    for (int i = 0; i < q; i++) {
        int l, r, k;
        scanf("%d %d %d", &l, &r, &k);
        rotate_left(&line[l], &line[r], k);
    }
    for (int i = 1; i <= n; i++) {
        printf("%d", line[i]);
        if (i < n) {
            printf(" ");
        }
    }
    printf("\n");
    return 0;
}

ข้อ 8 ชั้นหนังสือ

ระดับยาก เวลาไม่เกิน 1 วินาที

ห้องสมุดโรงเรียนมีชั้นหนังสือ K ชั้น หมายเลข 1 ถึง K ชั้นที่ i มีหนังสือ ni เล่ม เรียงจากซ้ายไปขวาเป็นเล่มที่ 1 ถึง ni แต่ละชั้นมีจำนวนเล่มไม่เท่ากัน ชมรมรักการอ่านนับไว้แล้วว่าหนังสือแต่ละเล่มมีกี่คำ และอยากรู้ว่าถ้าอ่านเป็นช่วงๆ จะต้องอ่านกี่คำ จึงมีคำถาม Q ข้อ แต่ละข้อถามว่าบนชั้น s หนังสือตั้งแต่เล่มที่ a ถึงเล่มที่ b รวมกันมีกี่คำ

ข้อมูลเข้า

บรรทัดแรกมีจำนวนเต็ม K (1 ≤ K ≤ 100000) อีก K บรรทัดต่อมา บรรทัดที่ i ขึ้นต้นด้วย ni (1 ≤ ni) ตามด้วยจำนวนคำของหนังสือเล่มที่ 1 ถึง ni บนชั้นที่ i แต่ละเล่มมี 1 ถึง 100000 คำ ผลรวมของ ni ทุกชั้นไม่เกิน 100000

บรรทัดถัดมามีจำนวนเต็ม Q (1 ≤ Q ≤ 100000) อีก Q บรรทัดต่อมา บรรทัดละสามจำนวน s a b โดยที่ 1 ≤ s ≤ K และ 1 ≤ a ≤ b ≤ ns

ข้อมูลออก

Q บรรทัด บรรทัดที่ j คือจำนวนคำรวมของคำถามที่ j

3
4 120 85 300 64
1 250
3 40 410 99
4
1 2 3
3 1 3
2 1 1
1 1 4
385
549
250
569
2
2 7 8
3 9 10 11
2
2 2 3
1 2 2
21
8
ดูคำใบ้

บวกทีละเล่มทุกคำถามช้าเกินไป ให้ทำผลรวมสะสมแบบหน้า 5.4 แยกทีละชั้น แต่ชั้นยาวไม่เท่ากัน ถ้าประกาศตาราง K × (จำนวนเล่มมากสุด) จะใหญ่ถึง 105 × 105 ช่อง ใช้ไม่ได้ ให้จองแบบหน้า 10.5 คือ long long **prefix ที่ชั้น i จอง ni + 1 ช่องพอดี ผลรวมของทั้งชั้นถึง 1010 เกิน int

ดูเฉลย
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int shelves;
    scanf("%d", &shelves);
    long long **prefix = malloc((shelves + 1) * sizeof(long long *));
    for (int s = 1; s <= shelves; s++) {
        int n;
        scanf("%d", &n);
        prefix[s] = malloc((n + 1) * sizeof(long long));
        prefix[s][0] = 0;
        for (int i = 1; i <= n; i++) {
            int words;
            scanf("%d", &words);
            prefix[s][i] = prefix[s][i - 1] + words;
        }
    }
    int q;
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        int s, a, b;
        scanf("%d %d %d", &s, &a, &b);
        printf("%lld\n", prefix[s][b] - prefix[s][a - 1]);
    }
    for (int s = 1; s <= shelves; s++) {
        free(prefix[s]);
    }
    free(prefix);
    return 0;
}