文章目录
T1. 扑克牌
小 P 从同学小 Q 那儿借来一副 n n n 张牌的扑克牌。
本题中我们不考虑大小王,此时每张牌具有两个属性:花色和点数。花色共有 4 4 4 种:方片、草花、红桃和黑桃。点数共有 13 13 13 种,从小到大分别为 A 2 3 4 5 6 7 8 9 T J Q K \tt A\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ T\ J\ Q\ K A 2 3 4 5 6 7 8 9 T J Q K。注意:点数 10 10 10 在本题中记为 T \tt T T。
我们称一副扑克牌是完整 的,当且仅当对于每一种花色和每一种点数,都恰好 有一张牌具有对应的花色和点数。由此,一副完整的扑克牌恰好有 4 × 13 = 52 4 × 13 = 52 4×13=52 张牌。以下图片展示了一副完整的扑克牌里所有的 52 52 52 张牌。

小 P 借来的牌可能不是完整的,为此小 P 准备再向同学小 S 借若干张牌。可以认为小 S 每种牌都有无限张,因此小 P 可以任意选择借来的牌。小 P 想知道他至少得向小 S 借多少张牌,才能让从小 S 和小 Q 借来的牌中,可以选出 52 52 52 张牌构成一副完整的扑克牌。
为了方便你的输入,我们使用字符 D \tt D D 代表方片,字符 C \tt C C 代表草花,字符 H \tt H H 代表红桃,字符 S \tt S S 代表黑桃,这样每张牌可以通过一个长度为 2 2 2 的字符串表示,其中第一个字符表示这张牌的花色,第二个字符表示这张牌的点数,例如 C A \tt CA CA 表示草花 A \tt A A, S T \tt ST ST 表示黑桃 T \tt T T(黑桃 10 10 10)。
时间限制:1 s
内存限制:512 MB
-
输入
输入的第一行包含一个整数 n n n 表示牌数。
接下来 n n n 行,每行包含一个长度为 2 2 2 的字符串描述一张牌,其中第一个字符描述其花色,第二个字符描述其点数。
-
输出
输出一行一个整数,表示最少还需要向小 S 借几张牌才能凑成一副完整的扑克牌。
-
样例输入 1
a1 SA -
样例输出 1
a51 -
样例输入 2
a4 DQ H3 DQ DT -
样例输出 2
a49 -
数据规模与约定
对于所有测试数据,保证: 1 ≤ n ≤ 52 1 ≤ n ≤ 52 1≤n≤52,输入的 n n n 个字符串每个都代表一张合法的扑克牌,即字符串长度为 2 2 2,且第一个字符为 D C H S \tt D\ C\ H\ S D C H S 中的某个字符,第二个字符为 A 2 3 4 5 6 7 8 9 T J Q K \tt A\ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ T\ J\ Q\ K A 2 3 4 5 6 7 8 9 T J Q K 中的某个字符。
思路分析
此题考查哈希表 / 集合的去重应用,属于入门题。
一副完整的扑克牌共有 52 52 52 张互不相同的牌。小 Q 拿到了 n n n 张牌,其中可能存在重复的牌。
- 可以使用 C++ STL 中的
std::set<string>存储所有输入的字符串(牌名),set会自动过滤掉重复的元素。 - 读入完所有 n n n 张牌后,
set的大小st.size()即为小 Q 实际拥有的不同扑克牌数量。 - 所需补充的牌数即为: 52 − st.size() 52 - \text{st.size()} 52−st.size()。
算法的时间复杂度为 O ( n log n ) O(n \log n) O(nlogn),空间复杂度为 O ( n ) O(n) O(n),对于 n ≤ 52 n \le 52 n≤52 可以在 1 ms 内运行完毕。
cpp
/*
* Name: T1.cpp
* Problem: 扑克牌
* Author: Teacher Gao.
* Date&Time: 2026/09/29 19:45
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
set<string> st;
for (int i = 0; i < n; i++) {
string s;
cin >> s;
st.insert(s);
}
cout << 52 - st.size() << "\n";
return 0;
}
T2. 地图探险
小 A 打算前往一片丛林去探险。丛林的地理环境十分复杂,为了防止迷路,他先派遣了一个机器人前去探路。
丛林的地图可以用一个 n n n 行 m m m 列的字符表来表示。我们将第 i i i 行第 j j j 列的位置的坐标记作 ( i , j ) ( 1 ≤ i ≤ n , 1 ≤ j ≤ m ) (i, j)(1 ≤ i ≤ n, 1 ≤ j ≤ m) (i,j)(1≤i≤n,1≤j≤m)。如果这个位置的字符为 x,即代表这个位置上有障碍,不可通过。反之,若这个位置的字符为 .,即代表这个位置是一片空地,可以通过。
这个机器人的状态由位置和朝向两部分组成。其中位置由坐标 ( x , y ) ( 1 ≤ x ≤ n , 1 ≤ y ≤ m ) (x, y)(1 ≤ x ≤ n, 1 ≤ y ≤ m) (x,y)(1≤x≤n,1≤y≤m) 刻画,它表示机器人处在地图上第 x x x 行第 y y y 列的位置。而朝向用一个 0 ∼ 3 0 \sim 3 0∼3 的整数 d d d 表示,其中 d = 0 d = 0 d=0 代表向东, d = 1 d = 1 d=1 代表向南, d = 2 d = 2 d=2 代表向西, d = 3 d = 3 d=3 代表向北。
初始时,机器人的位置为 ( x 0 , y 0 ) (x_0, y_0) (x0,y0),朝向为 d 0 d_0 d0。保证初始时机器人所在的位置为空地 。接下来机器人将要进行 k k k 次操作。每一步,机器人将按照如下的模式操作:
- 假设机器人当前处在的位置为 ( x , y ) (x, y) (x,y),朝向为 d d d。则它的方向上的下一步的位置 ( x ′ , y ′ ) (x^′, y^′) (x′,y′) 定义如下:
- 若 d = 0 d = 0 d=0,则令 ( x ′ , y ′ ) = ( x , y + 1 ) (x^′, y^′) = (x, y + 1) (x′,y′)=(x,y+1)
- 若 d = 1 d = 1 d=1,则令 ( x ′ , y ′ ) = ( x + 1 , y ) (x^′, y^′) = (x + 1, y) (x′,y′)=(x+1,y)
- 若 d = 2 d = 2 d=2,则令 ( x ′ , y ′ ) = ( x , y − 1 ) (x^′, y^′) = (x, y − 1) (x′,y′)=(x,y−1)
- 若 d = 3 d = 3 d=3,则令 ( x ′ , y ′ ) = ( x − 1 , y ) (x^′, y^′) = (x − 1, y) (x′,y′)=(x−1,y)
- 接下来,机器人判断它下一步的位置是否在地图内,且是否为空地。具体地说,它判断 ( x ′ , y ′ ) (x^′, y^′) (x′,y′) 是否满足 1 ≤ x ′ ≤ n , 1 ≤ y ′ ≤ m 1 ≤ x^′ ≤ n, 1 ≤ y^′ ≤ m 1≤x′≤n,1≤y′≤m,且 ( x ′ , y ′ ) (x^′, y^′) (x′,y′) 位置上是空地。如果条件成立,则机器人会向前走一步。它新的位置变为 ( x ′ , y ′ ) (x^′, y^′) (x′,y′),且朝向不变。如果条件不成立,则它会执行 "向右转" 操作。也就是说,令 d ′ = ( d + 1 ) m o d 4 d^′ = (d + 1) \mod 4 d′=(d+1)mod4(即
d + 1 d + 1 d+1 除以 4 4 4 的余数),且它所处的位置保持不变,但朝向由 d d d 变为 d ′ d^′ d′。
小 A 想要知道,在机器人执行完 k k k 步操作之后,地图上所有被机器人经过的位置(包括起始位置)有几个。
时间限制:1 s
内存限制:512 MB
-
输入
本题有多组测试数据 。
输入的第一行包含一个正整数 T T T,表示数据组数。
接下来包含 T T T 组数据,每组数据的格式如下:
第一行包含三个正整数 n , m , k n, m, k n,m,k。其中 n , m n,m n,m 表示地图的行数和列数, k k k 表示机器人执行操作的次数。
第二行包含两个正整数 x 0 , y 0 x_0, y_0 x0,y0 和一个非负整数 d 0 d_0 d0。
接下来 n n n 行,每行包含一个长度为 m m m 的字符串。保证字符串中只包含
x和.两个字符。其中,第 x x x 行的字符串的第 y y y 个字符代表的位置为 ( x , y ) (x, y) (x,y)。这个位置是x即代表它是障碍,否则代表它是空地。数据保证机器人初始时所在的位置为空地。 -
输出
对于每组数据:输出一行包含一个正整数,表示地图上所有被机器人经过的位置(包括起始位置)的个数。
-
样例输入
a2 1 5 4 1 1 2 ....x 5 5 20 1 1 0 ..... .xxx. .x.x. ..xx. x.... -
样例输出
a3 13 -
数据规模与约定
对于所有测试数据,保证: 1 ≤ T ≤ 5 , 1 ≤ n , m ≤ 10 3 , 1 ≤ k ≤ 10 6 , 1 ≤ x 0 ≤ n , 1 ≤ y 0 ≤ m , 0 ≤ d 0 ≤ 3 1 ≤ T ≤ 5,\ 1 ≤ n,m ≤ 10^3,\ 1 ≤ k ≤ 10^6,\ 1 ≤ x_0 ≤ n,\ 1 ≤ y_0 ≤ m,\ 0 ≤ d_0 ≤ 3 1≤T≤5, 1≤n,m≤103, 1≤k≤106, 1≤x0≤n, 1≤y0≤m, 0≤d0≤3,且机器人的起始位置为空地。
思路分析
此题考查网格图模拟与方向向量,属于基础题。
由于 k ≤ 10 6 k \le 10^6 k≤106,我们可以按照题目规则直接进行 k k k 次模拟。设置数组 dx[4] = {0, 1, 0, -1} 和 dy[4] = {1, 0, -1, 0},依次对应东、南、西、北。使用布尔数组 vis[N][N] 记录每个位置是否被访问过。初始时标记起点 vis[x0][y0] = true,并将答案计数器 ans 置为 1 1 1。计算下一步拟到达的位置 ( n x , n y ) = ( x + d x d , y + d y d ) (nx, ny) = (x + dxd, y + dyd) (nx,ny)=(x+dxd,y+dyd)。
- 若 1 ≤ n x ≤ n 1 \le nx \le n 1≤nx≤n 且 1 ≤ n y ≤ m 1 \le ny \le m 1≤ny≤m 且
grid[nx][ny] == '.',移动成功:更新 ( x , y ) = ( n x , n y ) (x, y) = (nx, ny) (x,y)=(nx,ny)。若该点此前未被访问,标记并使得ans++; - 否则,移动失败:更新方向 d = ( d + 1 ) m o d 4 d = (d + 1) \bmod 4 d=(d+1)mod4。
每组测试数据的时间复杂度为 O ( k ) O(k) O(k),空间复杂度为 O ( n × m ) O(n \times m) O(n×m),完全可以通过。
cpp
/*
* Name: T2.cpp
* Problem: 地图探险
* Author: Teacher Gao.
* Date&Time: 2026/09/29 19:50
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
void solve() {
char grid[MAXN][MAXN];
bool vis[MAXN][MAXN] = {0};
int n, m, k, x, y, d;
cin >> n >> m >> k >> x >> y >> d;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> grid[i][j];
}
}
vis[x][y] = true;
int ans = 1;
while (k--) {
int nx = x + dx[d];
int ny = y + dy[d];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && grid[nx][ny] == '.') {
x = nx;
y = ny;
if (!vis[x][y]) {
vis[x][y] = true;
ans++;
}
}
else {
d = (d + 1) % 4;
}
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
T3. 小木棍
小 S S S 喜欢收集小木棍。在收集了 n n n 根长度相等的小木棍之后,他闲来无事,便用它们拼起了数字。用小木棍拼每种数字的方法如下图所示。

现在小 S S S 希望拼出一个正整数,满足如下条件:
- 拼出这个数恰好 使用 n n n 根小木棍;
- 拼出的数没有前导 0 0 0;
- 在满足以上两个条件的前提下,这个数尽可能小。
小 S S S 想知道这个数是多少,可 n n n 很大,把木棍整理清楚就把小 S S S 折腾坏了,所以你需要帮他解决这个问题。如果不存在正整数满足以上条件,你需要输出 ‐1 进行报告。
时间限制:1 s
内存限制:512 MB
-
输入
本题有多组测试数据 。
输入的第一行包含一个正整数 T T T,表示数据组数。
接下来包含 T T T 组数据,每组数据的格式如下:
一行包含一个整数 n n n,表示木棍数。
-
输出
对于每组数据:输出一行,如果存在满足题意的正整数,输出这个数;否则输出
‐1。 -
样例输入
a5 1 2 3 6 18 -
样例输出
a-1 1 7 6 208 -
数据规模与约定
对于所有测试数据,保证: 1 ≤ T ≤ 50 1 ≤ T ≤ 50 1≤T≤50, 1 ≤ n ≤ 10 5 1 ≤ n ≤ 10^5 1≤n≤105。
思路分析
此题考查贪心算法与余数分类讨论,属于中等偏易题目。
要使拼出的数字尽可能小,有两个核心贪心策略:
- 位数越少越好 :消耗木棒最多的数字是
8(需 7 根)。因此对于大的 n n n,应该尽可能多地放置8来减少总位数。 - 高位数字越小越好 :首位不能为
0,其余位在允许的情况下优先选择较小的数字(如0消耗 6 根)。
我们可以观察 n m o d 7 n \bmod 7 nmod7 的余数情况:
- 若 n = 1 n = 1 n=1:无法拼出任何正整数,输出
-1。 - 若 n ≤ 7 n \le 7 n≤7:直接根据最小值特判:
- n = 2 → n=2 \to n=2→
2 - n = 3 → n=3 \to n=3→
7 - n = 4 → n=4 \to n=4→
4 - n = 5 → n=5 \to n=5→
2 - n = 6 → n=6 \to n=6→
6(首位不能为 0) - n = 7 → n=7 \to n=7→
8
- n = 2 → n=2 \to n=2→
- 若 n > 7 n > 7 n>7:计算额外需要处理的余数 n m o d 7 n \bmod 7 nmod7,优先处理最高 1 ∼ 2 1\sim 2 1∼2 位,后续位置全部填充
8:- n m o d 7 = 0 n \bmod 7 = 0 nmod7=0:全部填
8(如888...)。 - n m o d 7 = 1 n \bmod 7 = 1 nmod7=1:借 1 个 7 根(共 1 + 7 = 8 1+7=8 1+7=8 根),首两位拼出最小的
10,后续全部填8。 - n m o d 7 = 2 n \bmod 7 = 2 nmod7=2:首位填
1(耗 2 根),后续全部填8。 - n m o d 7 = 3 n \bmod 7 = 3 nmod7=3:借 1 个 7 根(共 3 + 7 = 10 3+7=10 3+7=10 根),首两位拼出最小的
22(或 200 等特例),最优前缀为200(需 12 根,借 2 个 7)或22(仅需 10 根,拼出22+ 8...,或200+ 8...)。更标准的表达为:余 3 时前缀选200(消耗 17 根,减去 2 个 8),或直接打表前缀:22开头再接 ( n − 10 ) / 7 (n-10)/7 (n−10)/7 个8。 - n m o d 7 = 4 n \bmod 7 = 4 nmod7=4:借 1 个 7 根(共 4 + 7 = 11 4+7=11 4+7=11 根),首两位拼出
20,后续全部填8。 - n m o d 7 = 5 n \bmod 7 = 5 nmod7=5:首位填
2(耗 5 根),后续全部填8。 - n m o d 7 = 6 n \bmod 7 = 6 nmod7=6:首位填
6(耗 6 根),后续全部填8。
- n m o d 7 = 0 n \bmod 7 = 0 nmod7=0:全部填
因此,可以根据 n m o d 7 n \bmod 7 nmod7 直接构造出对应的前缀和后接 8 的数量。
cpp
/*
* Name: T3.cpp
* Problem: 小木棍
* Author: Teacher Gao.
* Date&Time: 2026/09/29 20:03
*/
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
if (n < 2) {
cout << "-1\n";
return;
}
if (n == 2) { cout << "1\n"; return; }
if (n == 3) { cout << "7\n"; return; }
if (n == 4) { cout << "4\n"; return; }
if (n == 5) { cout << "2\n"; return; }
if (n == 6) { cout << "6\n"; return; }
if (n == 7) { cout << "8\n"; return; }
int rem = n % 7, e8 = n / 7;
if (rem == 0) {
for (int i = 0; i < e8; i++) cout << '8';
}
else if (rem == 1) {
cout << "10";
for (int i = 0; i < e8 - 1; i++) cout << '8';
}
else if (rem == 2) {
cout << '1';
for (int i = 0; i < e8; i++) cout << '8';
}
else if (rem == 3) {
if (e8 == 1) cout << "22";
else {
cout << "200";
for (int i = 0; i < e8 - 2; i++) cout << '8';
}
}
else if (rem == 4) {
cout << "20";
for (int i = 0; i < e8 - 1; i++) cout << '8';
}
else if (rem == 5) {
cout << '2';
for (int i = 0; i < e8; i++) cout << '8';
}
else if (rem == 6) {
cout << '6';
for (int i = 0; i < e8; i++) cout << '8';
}
cout << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
T4. 接龙
在玩惯了成语接龙之后,小 J 和他的朋友们发明了一个新的接龙规则。
总共有 n n n 个人参与这个接龙游戏,第 i i i 个人会获得一个整数序列 S i S_i Si 作为他的词库。一次游戏分为若干轮,每一轮规则如下:
- n n n 个人中的某个人 p p p 带着他的词库 S p S_p Sp 进行接龙。若这不是游戏的第一轮,那么这一轮进行接龙的人不能与上一轮相同,但可以与上上轮或更往前的轮相同。
- 接龙的人选择一个长度在 2 , k 2, k 2,k 的 S p S_p Sp 的连续子序列 A A A 作为这一轮的接龙序列 ,其中 k k k 是给定的常数。若这是游戏的第一轮,那么 A A A 需要以元素 1 1 1 开头,否则 A A A 需要以上一轮的接龙序列的最后一个元素开头。
序列 A A A 是序列 S S S 的连续子序列当且仅当可以通过删除 S S S 的开头和结尾的若干元素(可以不删除)得到 A A A。
为了强调合作,小 J 给了 n n n 个参与游戏的人 q q q 个任务,第 j j j 个任务需要这 n n n 个人进行一次游戏,在这次游戏里进行恰好 r j r_j rj 轮接龙,且最后一轮的接龙序列的最后一个元素恰好为 c j c_j cj。为了保证任务的可行性,小 J 请来你判断这 q q q 个任务是否可以完成的,即是否存在一个可能的游戏过程满足任务条件。
时间限制:2.0 s
内存限制:512 MB
-
输入
本题有多组测试数据 。
输入的第一行包含一个正整数 T T T,表示数据组数。
接下来包含 T T T 组数据,每组数据的格式如下:
第一行包含三个整数 n , k , q n, k, q n,k,q,分别表示参与游戏的人数、接龙序列长度上限以及任务个数。
接下来 n n n 行:第 i i i 行包含 ( l i + 1 ) (l_i + 1) (li+1) 个整数 l i , S i , 1 , S i , 2 , ⋅ ⋅ ⋅ , S i , l i l_i, S_{i,1}, S_{i,2}, · · · , S_{i,l_i} li,Si,1,Si,2,⋅⋅⋅,Si,li,其中第一个整数 l i l_i li 表示序列 S i S_i Si 的长度,接下来 l i l_i li 个整数描述序列 S i S_i Si。
接下来 q q q 行:第 j j j 行包含两个整数 r j , c j r_j , c_j rj,cj,描述一个任务。
-
输出
对于每个任务:输出一行包含一个整数,若任务可以完成输出 1 1 1,否则输出 0 0 0。
-
样例输入
a1 3 3 7 5 1 2 3 4 1 3 1 2 5 3 5 1 6 1 2 1 4 2 4 3 4 6 6 1 1 7 7 -
样例输出
a1 0 1 0 1 0 0 -
数据规模与约定
对于所有测试数据,保证:
- 1 ≤ T ≤ 5 1 ≤ T ≤ 5 1≤T≤5;
- 1 ≤ n ≤ 10 5 1 ≤ n ≤ 10^5 1≤n≤105, 2 ≤ k ≤ 2 × 10 5 2 ≤ k ≤ 2 × 10^5 2≤k≤2×105, 1 ≤ q ≤ 10 5 1 ≤ q ≤ 10^5 1≤q≤105;
- 1 ≤ l i ≤ 2 × 10 5 1 ≤ l_i ≤ 2 × 10^5 1≤li≤2×105, 1 ≤ S i , j ≤ 2 × 10 5 1 ≤ S_{i,j} ≤ 2 × 10^5 1≤Si,j≤2×105;
- 1 ≤ r j ≤ 10 2 1 ≤ r_j ≤ 10^2 1≤rj≤102, 1 ≤ c j ≤ 2 × 10 5 1 ≤ c_j ≤ 2 × 10^5 1≤cj≤2×105;
- 设 ∑ l \sum l ∑l 为单组测试数据内 所有 l i l_i li 的和,则 ∑ l ≤ 2 × 10 5 \sum l ≤ 2 × 10^5 ∑l≤2×105。
思路分析
此题考查动态规划与滑动区间标记,属于提高组难度的综合题。
根据接龙规则:
- 连续两轮不能由同一个人接龙 :如果第 r − 1 r-1 r−1 轮的结尾数字 v v v 是由玩家 p p p 拼出的,那么第 r r r 轮中除了玩家 p p p 以外的其他人 都可以用 v v v 作为开头。
- 多玩家接龙无限制 :如果第 r − 1 r-1 r−1 轮的结尾数字 v v v 可以由至少两个不同玩家 拼出,那么第 r r r 轮中所有人 都可以用 v v v 作为开头。
定义数组 f[r][j] 表示在第 r r r 轮中,以数字 j j j 结尾时的玩家可行性状态:
f[r][j] = -1:表示第 r r r 轮无法以数字 j j j 结尾(不可达)。f[r][j] = 0:表示第 r r r 轮可以以数字 j j j 结尾,且有多个不同玩家都能接出该数字(下一轮所有人皆可接龙)。f[r][j] = i(正整数):表示第 r r r 轮可以以数字 j j j 结尾,但只有玩家 i i i 能接出该数字(下一轮除了玩家 i i i 以外的人皆可接龙)。
对于第 r r r 轮,我们依次遍历每一位玩家 i i i 和他的序列 a i ai ai:
- 维护一个变量
L(表示当前位置向后还能延伸的合法接龙步数)。 - 每往后走一步,
L--。只要L > 0,说明当前位置 a i j aij aij 处于某个合法的接龙区间内,此时该位置可以作为第 r r r 轮的结尾:- 若
f[r][a[i][j]] == -1,说明这是第 r r r 轮首次有人接出数字 a i j aij aij,记录f[r][a[i][j]] = i。 - 若
f[r][a[i][j]] != -1且不等于 i i i,说明之前已有其他玩家接出了该数字,更新f[r][a[i][j]] = 0(标记为多人可行)。
- 若
- 当扫描到某个数字 a i j aij aij 时,检查它是否可以作为第 r r r 轮的起点 (即
f[r-1][a[i][j]] != -1且不等于 i i i)。如果可以,则将步数重新重置为L = k!
外层循环最多 100 轮,内层循环扫描所有玩家的序列总长度 ∑ l i \sum l_i ∑li。总时间复杂度为 O ( 100 × ∑ l i ) O(100 \times \sum l_i) O(100×∑li),单次询问响应 O ( 1 ) O(1) O(1)。空间复杂度: O ( 100 × max ( c ) ) O(100 \times \max(c)) O(100×max(c))。
cpp
/*
* Name: T4.cpp
* Problem: 接龙
* Author: Teacher Gao.
* Date&Time: 2026/09/29 20:31
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
vector<int> a[N];
int f[105][N << 1];
int main() {
int t;
if (scanf("%d", &t) == EOF) return 0;
while (t--) {
int n, k, q, s, tmp, x, y;
scanf("%d%d%d", &n, &k, &q);
for (int i = 1; i <= n; i++) {
a[i].clear();
scanf("%d", &s);
for (int j = 1; j <= s; j++) {
scanf("%d", &tmp);
a[i].push_back(tmp);
}
}
memset(f, -1, sizeof(f));
f[0][1] = 0;
for (int r = 1; r <= 100; r++) { // 1. 枚举轮次
for (int i = 1; i <= n; i++) { // 2. 枚举接龙的人
int L = 0; // 当前可延伸的剩余步数
for (int j = 0; j < a[i].size(); j++) { // 3. 枚举第 i 个人的词库
L--;
// 如果还在合法的接龙步数范围内,尝试更新第 r 轮结尾状态
if (L > 0) {
if (f[r][a[i][j]] == -1) { // 第一次出现该结尾
f[r][a[i][j]] = i; // 记录接龙玩家编号
}
else if (f[r][a[i][j]] != i) { // 已经被其他玩家接出过
f[r][a[i][j]] = 0; // 标记为多人可接龙
}
}
// 检查当前单词能否作为第 r-1 轮转移过来的合法开头(上一轮可达且不是当前玩家 i 结尾)
if (f[r-1][a[i][j]] != -1 && f[r-1][a[i][j]] != i) {
L = k; // 重置最大延伸距离为 k
}
}
}
}
for (int i = 1; i <= q; i++) {
scanf("%d%d", &x, &y);
if (f[x][y] == -1) printf("0\n");
else printf("1\n");
}
}
return 0;
}