温暖的签到题。
#include
using namespace std ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
int n ;
cin >> n ;
int flag = 0 ;
for(int i = 1 ; i * i <= n ; i ++)
{
if(n % i == 0)
{
int j = n / i ;
if(min(i , j) * 2 >= max(i , j)) flag = 1 ;
}
}
cout << flag << '\n' ;
}
return 0 ;
}
温暖的签到题。
#include
using namespace std ;
set s ;
int n , a[10] ;
int p[10] ;
void cal1(int l1 , int r1 , int l2 , int r2)
{
int res1 = 0 ;
for(int i = l1 ; i <= r1 ; i ++) res1 = res1 * 10 + a[p[i]] ;
int res2 = 0 ;
for(int i = l2 ; i <= r2 ; i ++) res2 = res2 * 10 + a[p[i]] ;
if(res1 + res2 >= 0) s.insert(res1 + res2) ;
if(res1 - res2 >= 0) s.insert(res1 - res2) ;
if(res1 * res2 >= 0) s.insert(res1 * res2) ;
}
void cal2(int l1 , int r1 , int l2 , int r2 , int l3 , int r3)
{
int res1 = 0 ;
for(int i = l1 ; i <= r1 ; i ++) res1 = res1 * 10 + a[p[i]] ;
int res2 = 0 ;
for(int i = l2 ; i <= r2 ; i ++) res2 = res2 * 10 + a[p[i]] ;
int res3 = 0 ;
for(int i = l3 ; i <= r3 ; i ++) res3 = res3 * 10 + a[p[i]] ;
if(res1 + res2 + res3 >= 0) s.insert(res1 + res2 + res3) ;
if(res1 + res2 - res3 >= 0) s.insert(res1 + res2 - res3) ;
if(res1 + res2 * res3 >= 0) s.insert(res1 + res2 * res3) ;
if(res1 - res2 + res3 >= 0) s.insert(res1 - res2 + res3) ;
if(res1 - res2 - res3 >= 0) s.insert(res1 - res2 - res3) ;
if(res1 - res2 * res3 >= 0) s.insert(res1 - res2 * res3) ;
if(res1 * res2 + res3 >= 0) s.insert(res1 * res2 + res3) ;
if(res1 * res2 - res3 >= 0) s.insert(res1 * res2 - res3) ;
if(res1 * res2 * res3 >= 0) s.insert(res1 * res2 * res3) ;
}
void cal3()
{
int res1 = a[p[1]] ;
int res2 = a[p[2]] ;
int res3 = a[p[3]] ;
int res4 = a[p[4]] ;
if(res1 + res2 + res3 + res4 >= 0) s.insert(res1 + res2 + res3 + res4) ;
if(res1 + res2 - res3 + res4 >= 0) s.insert(res1 + res2 - res3 + res4) ;
if(res1 + res2 * res3 + res4 >= 0) s.insert(res1 + res2 * res3 + res4) ;
if(res1 - res2 + res3 + res4 >= 0) s.insert(res1 - res2 + res3 + res4) ;
if(res1 - res2 - res3 + res4 >= 0) s.insert(res1 - res2 - res3 + res4) ;
if(res1 - res2 * res3 + res4 >= 0) s.insert(res1 - res2 * res3 + res4) ;
if(res1 * res2 + res3 + res4 >= 0) s.insert(res1 * res2 + res3 + res4) ;
if(res1 * res2 - res3 + res4 >= 0) s.insert(res1 * res2 - res3 + res4) ;
if(res1 * res2 * res3 + res4 >= 0) s.insert(res1 * res2 * res3 + res4) ;
if(res1 + res2 + res3 - res4 >= 0) s.insert(res1 + res2 + res3 - res4) ;
if(res1 + res2 - res3 - res4 >= 0) s.insert(res1 + res2 - res3 - res4) ;
if(res1 + res2 * res3 - res4 >= 0) s.insert(res1 + res2 * res3 - res4) ;
if(res1 - res2 + res3 - res4 >= 0) s.insert(res1 - res2 + res3 - res4) ;
if(res1 - res2 - res3 - res4 >= 0) s.insert(res1 - res2 - res3 - res4) ;
if(res1 - res2 * res3 - res4 >= 0) s.insert(res1 - res2 * res3 - res4) ;
if(res1 * res2 + res3 - res4 >= 0) s.insert(res1 * res2 + res3 - res4) ;
if(res1 * res2 - res3 - res4 >= 0) s.insert(res1 * res2 - res3 - res4) ;
if(res1 * res2 * res3 - res4 >= 0) s.insert(res1 * res2 * res3 - res4) ;
if(res1 + res2 + res3 * res4 >= 0) s.insert(res1 + res2 + res3 * res4) ;
if(res1 + res2 - res3 * res4 >= 0) s.insert(res1 + res2 - res3 * res4) ;
if(res1 + res2 * res3 * res4 >= 0) s.insert(res1 + res2 * res3 * res4) ;
if(res1 - res2 + res3 * res4 >= 0) s.insert(res1 - res2 + res3 * res4) ;
if(res1 - res2 - res3 * res4 >= 0) s.insert(res1 - res2 - res3 * res4) ;
if(res1 - res2 * res3 * res4 >= 0) s.insert(res1 - res2 * res3 * res4) ;
if(res1 * res2 + res3 * res4 >= 0) s.insert(res1 * res2 + res3 * res4) ;
if(res1 * res2 - res3 * res4 >= 0) s.insert(res1 * res2 - res3 * res4) ;
if(res1 * res2 * res3 * res4 >= 0) s.insert(res1 * res2 * res3 * res4) ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
for(int i = 1 ; i <= 4 ; i ++) cin >> a[i] ;
for(int i = 1 ; i <= 4 ; i ++) p[i] = i ;
do
{
for(int i = 1 ; i <= 3 ; i ++) cal1(1 , i , i + 1 , 4) ;
for(int i = 1 ; i <= 4 ; i ++) for(int j = i + 1 ; j <= 4 ; j ++)
if(i + 1 <= j && j + 1 <= 4)
cal2(1 , i , i + 1 , j , j + 1 , 4) ;
cal3() ;
} while(next_permutation(p + 1 , p + 4 + 1)) ;
cout << s.size() << '\n' ;
return 0 ;
}
Problem C Pyramid
考虑前个小球经过后每个节点的开关状态,也就是每个节点有多少个小球经过。
表示第层第个节点有多少个小球经过。
在知道开关状态后就可以模拟第个小球的移动过程了。
#include
using namespace std ;
const int maxn = 10000 + 10 ;
int dp[2][maxn] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
int n , k ;
cin >> n >> k ;
if(n == 1)
{
cout << "0\n" ;
continue ;
}
n -- ;
memset(dp , 0 , sizeof(dp)) ;
int now = 0 ;
dp[0][0] = k - 1 ;
for(int i = 0 ; i < n ; i ++)
{
memset(dp[1 - i % 2] , 0 , sizeof(dp[1 - i % 2])) ;
for(int j = 0 ; j <= i ; j ++)
{
dp[1 - i % 2][j] += (dp[i % 2][j] + 1) / 2 ;
dp[1 - i % 2][j + 1] += dp[i % 2][j] / 2 ;
}
if(dp[i % 2][now] % 2 == 1) now ++ ;
}
cout << now << '\n' ;
}
return 0 ;
}
留坑。
表示这段区间内只剩下第种颜色时第种颜色的个数最大值。
表示这段区间可以全部清除。
转移就套区间板子即可。颜色相同的两个区间才可以合并。
#include
using namespace std ;
const int maxn = 500 + 10 ;
int n , m ;
char s[maxn] ;
int a[maxn] ;
int dp[maxn][maxn][7] ;
bool ok[maxn][maxn] ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
cin >> s + 1 >> m ;
n = strlen(s + 1) ;
for(int i = 1 ; i <= n ; i ++)
{
if(s[i] == 'R') a[i] = 0 ;
else if(s[i] == 'G') a[i] = 1 ;
else if(s[i] == 'B') a[i] = 2 ;
else if(s[i] == 'C') a[i] = 3 ;
else if(s[i] == 'M') a[i] = 4 ;
else if(s[i] == 'Y') a[i] = 5 ;
else a[i] = 6 ;
}
if(m == 1)
{
cout << "Yes\n" ;
return 0 ;
}
for(int i = 1 ; i <= n ; i ++)
for(int j = i ; j <= n ; j ++)
for(int k = 0 ; k < 7 ; k ++)
dp[i][j][k] = -1e9 ;
for(int i = 1 ; i <= n ; i ++) dp[i][i][a[i]] = 1 ;
for(int len = 2 ; len <= n ; len ++)
for(int i = 1 ; i + len - 1 <= n ; i ++)
{
int j = i + len - 1 ;
for(int k = i ; k + 1 <= j ; k ++)
{
for(int c = 0 ; c < 7 ; c ++)
{
if(dp[i][k][c] > 0 && dp[k + 1][j][c] > 0)
dp[i][j][c] = max(dp[i][j][c] , dp[i][k][c] + dp[k + 1][j][c]) ;
if(ok[i][k])
dp[i][j][c] = max(dp[i][j][c] , dp[k + 1][j][c]) ;
if(ok[k + 1][j])
dp[i][j][c] = max(dp[i][j][c] , dp[i][k][c]) ;
}
}
for(int c = 0 ; c < 7 ; c ++)
if(dp[i][j][c] >= m) ok[i][j] = true ;
}
if(ok[1][n]) cout << "Yes\n" ;
else cout << "No\n" ;
return 0 ;
}
Problem F Cable Protection
求基环树的最小点覆盖
先考虑树的情况,树型dp即可
考虑环上的一条边,则u和v至少有一个点要选,那么我们钦定u必选然后断掉边跑一遍树型dp,再钦定v必选跑一遍树型dp,两者取优即可
#include
using namespace std;
const int N = 2e5 + 5;
int n, m, dp[N][2];
vector G[N];
void dfs(int u, int fa, int rt)
{
int sum1 = 0, summn = 0;
for(int v : G[u])
{
if(v==fa||v==rt) continue;
dfs(v, u, rt);
sum1 += dp[v][1], summn += min(dp[v][0], dp[v][1]);
}
dp[u][0] = sum1;
dp[u][1] = summn + 1;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=1; i<=n+m; i++)
{
int u, v; scanf("%d%d", &u, &v);
++u, ++v;
G[u].push_back(v), G[v].push_back(u);
}
int ans = 0;
dfs(1, n, 1);
ans = dp[1][1];
dfs(n, 1, n);
ans = min(ans, dp[n][1]);
printf("%d\n", ans);
return 0;
}
判定基环树同构
先找到环,那么对于环上的每个点的子树做一遍树哈希,问题就转化为了判断两个环旋转同构。
利用最小表示法,把环变成链后再哈希即可(需要正反各做一遍)
树哈希的方法有很多
第一种需要先按排序,第二三种不需要。
本题数据较强,23需要一起用才能过
链哈希,就直接让,由于树哈希后值都很大,可以利用map离散化成小数,可以降低冲突率
#include
#define fi first
#define se second
using namespace std;
using pii = pair;
const int N = 1e6 + 5, mod = 998244353, mod2 = 1e9 + 7, base = 19260817;
map lsh;
set st;
int tot;
int pr[N];
void getp(int n)
{
static bool vis[N];
for(int i=2; i<=n; i++)
{
if(!vis[i]) pr[++pr[0]] = i;
for(int j=1; j<=pr[0]&&1ll*pr[j]*i<=n; j++)
{
vis[pr[j]*i] = 1;
if(i%pr[j]==0) break;
}
}
}
int minshift(vector &s)
{
int n = s.size();
int i = 0, j = 1, k = 0;
while(i0) i += k + 1;
else j += k + 1;
if(i==j) j++;
k = 0;
}
}
return i < j ? i : j;
}
struct Tree
{
vector G[N], loop;
bool vis[N];
int n, in[N], sz[N];
pii hval[N];
void init()
{
scanf("%d", &n);
for(int i=1; i<=n; i++) G[i].clear(), in[i] = vis[i] = 0;
for(int i=1; i<=n; i++)
{
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
++in[u], ++in[v];
}
}
void findloop()
{
queue q;
for(int i=1; i<=n; i++) if(in[i]==1) q.push(i);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int v : G[u])
{
--in[v];
if(in[v]==1) q.push(v);
}
}
loop.clear();
int st = 0;
for(int i=1; i<=n; i++)
if(in[i]==2)
{
st = i;
break;
}
while(!vis[st])
{
loop.push_back(st);
vis[st] = 1;
for(int v : G[st])
if(in[v]==2&&!vis[v])
{
st = v;
break;
}
}
}
void dfs(int u, int fa)
{
hval[u].fi = hval[u].se = sz[u] = 1;
for(int v : G[u])
if(v!=fa&&!vis[v])
{
dfs(v, u);
sz[u] += sz[v];
hval[u].fi = (hval[u].fi + 1ll*hval[v].fi*pr[sz[v]]%mod)%mod;
hval[u].se = 1ll*hval[u].se*(hval[v].se + pr[sz[v]])%mod2;
}
}
void geth()
{
findloop();
for(int &x : loop)
{
dfs(x, 0);
auto hv = hval[x];
if(!lsh.count(hv)) lsh[hv] = ++tot;
x = lsh[hv];
}
int idx = minshift(loop), len = loop.size();
int hsh1 = 0;
for(int i=0; ihsh2) swap(hsh1, hsh2);
st.insert({hsh1, hsh2});
}
}t;
void solve()
{
lsh.clear(); tot = 0;
st.clear();
int cnt; scanf("%d", &cnt);
for(int i=1; i<=cnt; i++)
{
t.init();
t.geth();
}
printf("%d\n", (int)st.size());
}
int main()
{
getp(N-1);
int _; scanf("%d", &_);
while(_--) solve();
return 0;
}
在无向带权图上,求一棵生成树,在最大化最小边权的前提下,最小化,其中$f(i,j)$定义为i和j路径上边权最小的边
先求一棵最大生成树,那么我们该树中的最小边权就是结果的最小边权
之后要满足第二个条件,直觉就是边权越小越好,所以从该条边权开始再求一遍最小生成树即可
#include
using namespace std;
using ll = long long;
const int N = 5e5 + 5;
struct edge
{
int u, v, w;
bool operator < (const edge& oth) { return w > oth.w; }
}e[N], tr[N];
int n, m;
int fa[N], sz[N];
void init(int n)
{
iota(fa+1, fa+n+1, 1);
for(int i=1; i<=n; i++) sz[i] = 1;
}
int find(int x) { return x==fa[x] ? x : fa[x] = find(fa[x]); }
bool merge(int x, int y)
{
int fx = find(x), fy = find(y);
if(fx!=fy)
{
fa[fx] = fy;
sz[fy] += sz[fx];
return true;
}
return false;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i=1; i<=m; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
sort(e+1, e+m+1);
int cnt = 0;
init(n);
for(int i=1; i<=m; i++)
{
cnt += merge(e[i].u, e[i].v);
if(cnt==n-1)
{
init(n);
cnt = 0;
for(int j=i; j>=1; j--) if(merge(e[j].u, e[j].v)) tr[++cnt] = e[j];
break;
}
}
reverse(tr+1, tr+cnt+1);
init(n);
ll ans = 0;
for(int i=1; i<=cnt; i++)
{
ans += 1ll*sz[find(tr[i].u)]*sz[find(tr[i].v)]*tr[i].w;
merge(tr[i].u, tr[i].v);
}
printf("%lld\n", ans);
return 0;
}
题目难度主要在读题上,因为对连通图那一套概念忘完了。所以题目一直读不懂。
给你一张无向图。问你割点数量、割边数量、点双个数、点双的边的数量的最大值。
#include
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define cl(x) x.clear()
#define all(x) x.begin() , x.end()
#define rep(i , x , n) for(int i = x ; i <= n ; i ++)
#define per(i , n , x) for(int i = n ; i >= x ; i --)
#define mem0(x) memset(x , 0 , sizeof(x))
#define mem_1(x) memset(x , -1 , sizeof(x))
#define mem_inf(x) memset(x , 0x3f , sizeof(x))
#define debug(x) cerr << #x << " = " << x << '\n'
#define ddebug(x , y) cerr << #x << " = " << x << " " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair pii ;
typedef pair pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 1000 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ;
#include
using namespace std ;
const int maxm = 2e6 + 10 ;
int n , m ;
int head[maxn] ;
int cnt = 1 ;
int dfn[maxn] , low[maxn] , parent[maxn] ;
int ans = 0 ;
bool cut[maxn] ;
int ans1 , ans2 , ans3 , ans4 ;
bool ok[maxn][maxn] ;
bool vis[maxn] ;
int c[maxn] ;
vector> tmp ;
int res ;
int num[maxn] ;
int stk[maxn] ;
int top ;
vector vcc ;
bool used[maxn] ;
struct Node
{
int v , next ;
} edge[maxm] ;
void add(int u , int v)
{
edge[cnt].v = v ;
edge[cnt].next = head[u] ;
head[u] = cnt ++ ;
}
void update()
{
for(auto u : vcc) used[u] = true ;
int res = 0 ;
for(auto u : vcc) for(int i = head[u] ; i != 0 ; i = edge[i].next)
{
int v = edge[i].v ;
if(used[v]) res ++ ;
}
res /= 2 ;
ans4 = max(ans4 , res) ;
for(auto u : vcc) used[u] = false ;
}
void dfs(int u)
{
static int count = 0 ;
int i , j ;
int children = 0 ;
int v ;
dfn[u] = low[u] = ++ count ;
for (i = head[u] ; i != 0 ; i = edge[i].next)
{
v = edge[i].v ;
if (dfn[v] == 0)
{
stk[++top]=edge[i].v;
children ++ ;
parent[v] = u ;
dfs(v) ;
low[u] = min(low[u] , low[v]) ;
if(cut[u] == false && (parent[u] == -1 && children >= 2 || parent[u] != -1 && low[v] >= dfn[u]))
cut[u] = true , ans1 ++ ;
if(low[v] > dfn[u]) tmp.push_back({u , v}) , ok[u][v] = true , ans2 ++ ;
if(low[v] >= dfn[u])
{
ans3 ++ ;
vcc.clear() ;
while(stk[top]!=edge[i].v)//将点出栈直到目标点
vcc.push_back(stk[top--]);
vcc.push_back(stk[top--]);//目标点出栈
vcc.push_back(u);//不要忘了将当前点存入bcc
update() ;
}
}
else if (v != parent[u])
low[u] = min(low[u], dfn[v]) ;
}
}
// void dfs2(int u)
// {
// vis[u] = true ;
// c[u] = ans3 ;
// for(int i = head[u] ; i != 0 ; i = edge[i].next)
// {
// int v = edge[i].v ;
// if(!vis[v] && !ok[u][v] && !ok[v][u])
// dfs2(v) ;
// }
// }
int main()
{
ios ;
int T ;
cin >> T ;
while(T --)
{
cin >> n >> m ;
memset(head , 0 , sizeof(head)) ;
memset(parent , -1 , sizeof(parent)) ;
memset(dfn , 0 , sizeof(dfn)) ;
memset(cut , false , sizeof(cut)) ;
memset(vis , false , sizeof(vis)) ;
memset(c , 0 , sizeof(c)) ;
memset(num , 0 , sizeof(num)) ;
for(int i = 1 ; i <= m ; i ++)
{
int u , v ;
cin >> u >> v ;
add(u , v) ;
add(v , u) ;
}
ans1 = 0 , ans2 = 0 ;
ans3 = 0 , ans4 = 0 ;
for(auto now : tmp) ok[now.first][now.second] = false ;
tmp.clear() ;
for(int i = 1 ; i <= n ; i ++) if(dfn[i] == 0) stk[top=1]=i,dfs(i) ;
//cout << ans3 << '\n' ;
// for(int i = 1 ; i <= n ; i ++)
// if(!vis[i])
// ans3 ++ , dfs2(i) ;
// for(int i = 1 ; i <= n ; i ++)
// for(int j = head[i] ; j != 0 ; j = edge[j].next)
// if(c[i] == c[edge[j].v])
// num[c[i]] ++ ;
//for(int i = 1 ; i <= ans3 ; i ++) ans4 = max(ans4 , num[i]) ;
//cout << "??? " << tmp.size() << '\n' ;
//ans3 += ans2 ;
//ans4 /= 2 ;
int gcd = __gcd(ans3 , ans4) ;
ans3 /= gcd ;
ans4 /= gcd ;
cout << ans1 << ' ' << ans2 << ' ' << ans3 << ' ' << ans4 << '\n' ;
}
return 0 ;
}
留坑。
定义一种数字是好的,当且仅当各个数位两两不同(无前导0)
现在问你L到R之间好的数字的个数,或问你第x个好的数字是什么
并且询问有可能是10进制,也可能是16进制
相当于有4个子问题。
先解决0到x范围内有多少个好的数字:考虑到数字种类数很少,所以状压一下即可。
那么第一种问题直接差分一下,第二种问题就从高位到低位,从小到大枚举当前位置的数,然后求出以当前为前缀时的方案数,超过了x就填。
16进制的处理,可以直接用%llx进行scanf和printf,不放心就手写下进制转化
#include
using namespace std;
using ll = long long;
using ull = unsigned long long;
char s[105], op[2], ans[105];
bool dflag;
int type;
ull dp[2][20][1<<16];
int convert(char c)
{
if(isdigit(c)) return c - '0';
return c - 'a' + 10;
}
char convert2(int x)
{
if(x<=9) return char('0' + x);
return char('a' + x - 10);
}
ull conv10(char *s)
{
ull x = 0;
int n = strlen(s);
for(int i=0; i=1; i--) putchar(stk[i]);
puts("");
}
}
ull DP(int b, int lim, int z, int st)
{
if(b<0) return 1;
auto &x = dp[dflag][b][st];
if(!lim&&!z&&~x) return x;
int up = lim ? convert(s[b]) : (dflag ? 9 : 15);
ull ans = 0;
for(int i=0; i<=up; i++)
{
if(z&&!i) ans += DP(b-1, lim&&i==up, z, st);
else
{
if((st>>i)&1) continue;
ans += DP(b-1, lim&&i==up, 0, st|(1<10) return DP(9, 0, 1, 0);
if(!dflag && n>16) return DP(15, 0, 1, 0);
return DP(n-1, 1, 1, 0);
}
int check(char *s)
{
int st = 0, n = strlen(s);
for(int i=0; i>d)&1) return 0;
st |= (1<=0; i--) putchar(convert2(ans[i]));
puts("");
return;
}
int up = dflag ? 9 : 15;
for(int i=0; i<=up; i++)
{
ans[b] = i;
if(z&&!i)
{
ull cnt = DP(b-1, 0, z, st);
if(cnt>i)&1) continue;
ull cnt = DP(b-1, 0, 0, st|(1<
留坑。
温暖的签到题。
#include
using namespace std ;
int row[] = {0 , 0 , 1 , 1} ;
int col[] = {0 , 1 , 0 , 1} ;
bool c[2] ;
bool d[2] ;
bool tc[2] ;
bool td[2] ;
int a[10] ;
int res ;
void dfs(int now)
{
//a[1] = 1 , a[3] = 1 ;
if(now > 4)
{
tc[0] = tc[1] = td[0] = td[1] = false ;
for(int i = 1 ; i <= 4 ; i ++)
if(a[i] == 1)
{
tc[(i - 1) / 2] = true ;
td[(i - 1) % 2] = true ;
}
if(c[0] == tc[0] && c[1] == tc[1] && d[0] == td[0] && d[1] == td[1]) res ++ ;
return ;
}
for(int i = 0 ; i < 2 ; i ++) a[now] = i , dfs(now + 1) ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int T ;
cin >> T ;
while(T --)
{
int n , m ;
cin >> n >> m ;
c[0] = c[1] = d[0] = d[1] = false ;
for(int i = 1 ; i <= n ; i ++)
{
int x ;
cin >> x ;
c[x] = true ;
}
for(int i = 1 ; i <= m ; i ++)
{
int x ;
cin >> x ;
d[x] = true ;
}
res = 0 ;
dfs(1) ;
cout << res << '\n' ;
}
return 0 ;
}