10.6 แบบฝึกหัด
เขียนแต่ละข้อเป็นไฟล์ใหม่ใน Code::Blocks รันแล้วเทียบกับผลลัพธ์ที่ต้องได้ให้ตรงทุกตัวอักษร รวมถึงช่องว่างและการขึ้นบรรทัด ติดจริงๆ ค่อยเปิดคำใบ้ และเปิดเฉลยหลังทำเสร็จแล้วเท่านั้น
ข้อ 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 56704 6705 6703 6702 67013 2
31 27 45
2 2
3 145 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 รายงานเลขโดด
ครูคณิตศาสตร์ให้ตัวเลขมา 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
10004 22 2 9
1 7 7 7
4 1 0 12
0
980891 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 ชื่อไฟล์และนามสกุล
ชมรมถ่ายภาพเก็บไฟล์ไว้ในหลายโฟลเดอร์ ที่อยู่ของไฟล์เขียนเป็น 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.ctrip jpg
readme -
notes -
swap c2
music/song.final.mp3
a.bsong.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 เก็บช่องว่าง
น้องคัดลอกข้อความจากเอกสารหลายไฟล์มาต่อกัน แล้วได้ช่องว่างเกินมาเต็มไปหมด ทั้งหน้าข้อความ ท้ายข้อความ และระหว่างคำที่บางที่มีหลายช่อง ให้จัดข้อความใหม่ให้ไม่มีช่องว่างหน้าและท้ายข้อความ และระหว่างคำมีช่องว่างแค่ช่องเดียว ตัวอักษรอื่นต้องอยู่ครบตามลำดับเดิม
ข้อนี้ให้ฝึกเขียนฟังก์ชัน void tidy(char *s) ที่แก้สตริงในอาเรย์เดิม โดยไม่ใช้อาเรย์ที่สอง
ข้อมูลเข้า
ข้อความหนึ่งบรรทัด ยาว 1 ถึง 1000 ตัว ประกอบด้วยตัวอักษรภาษาอังกฤษ ตัวเลข เครื่องหมายวรรคตอน และช่องว่าง อาจมีแต่ช่องว่างทั้งบรรทัดก็ได้
ข้อมูลออก
ข้อความที่จัดแล้วหนึ่งบรรทัด ถ้าข้อความมีแต่ช่องว่าง ให้พิมพ์บรรทัดว่าง
Hello, my name is Somchai. Hello, my name is Somchai.Grade:A Score: 95Grade: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 จับฉลากของขวัญ
ปีใหม่นี้โรงเรียนให้ทุกห้องจับฉลากแลกของขวัญ ห้องที่มีนักเรียน 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
12 4 1 3
12
3
2 3 1
2
2 13 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 สลับแถวในตาราง
ครูเก็บคะแนนเก็บของนักเรียนไว้ในตาราง 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 410 10 9
3 4 8
5 7 6
8 9 101 5 1
100 0 50 75 25
1 1100 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 หมุนกลุ่มในแถว
หน้าเสาธงมีนักเรียนเข้าแถวตอนเรียงหนึ่ง 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 1016 12 13 17 11 14 153 2
5 4 3
1 3 0
2 2 75 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 ชั้นหนังสือ
ห้องสมุดโรงเรียนมีชั้นหนังสือ 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 4385
549
250
5692
2 7 8
3 9 10 11
2
2 2 3
1 2 221
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;
}