Problem Description
一天,一只住在 501 的皮卡丘决定发奋学习,成为像 LeiQ 一样的巨巨,于是他向镇上的贤者金桔请教如何才能进化成一只雷丘。
金桔告诉他需要进化石才能进化,并给了他一个地图,地图上有 n 个小镇,并且标注了每个小镇上可收集的进化石。
但是皮卡丘拿到地图就蒙圈了,他可不知道自己到底需要哪种进化石,而且由于经费有限,他只能去某几个相邻的小镇,所以他机智地找到了善于编程的你,询问你这些镇上可收集的进化石有哪些,然后再自己决定行程。
Input
首先输入一个整数 T (1 <= T <= 10),代表有 T 组数据。
每组数据的第一行输入一个整数 n (1 <= n <= 100000),代表有 n 个小镇。
接下来的 n 行表示第 1 个到第 n 个的小镇的信息。每行先输入一个整数 m (0 <= m <= 30),代表这个小镇上进化石的种类数,紧接着输入 m 个整数,代表进化石的种类编号(编号从 1 开始,不超过 30)。
之后的一行输入一个整数 q (1 <= q <= 25000),代表皮卡丘有 q 次询问。
接下来的 q 行每行输入两个整数 l, r (1 <= l <= r <= n),表示他想询问从第 l 个到第 r 个小镇上可收集的进化石有哪几种。
Output
对于每组输入,首先输出一行 "Case T:",表示当前是第几组数据。
接下来对于每一次询问,按编号升序输出所有可收集的进化石。如果没有进化石可收集,则输出一个小豪的百分号 “%”(不要问我为什么,出题就是这么任性)。
Sample Input
1
3
Sample Output
Case 1:
1 2 3 4 10
1 2 4
%
Hint
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 100001;
int st[MAX<<2];
void Initialize();
void Update(int node, int l, int r, int index, int value);
int Query(int node, int l, int r, int il, int ir);
int main(int argc, char const *argv[])
{
int t, n, m, q, l, r;
scanf("%d", &t);
for(int i=1; i<=t; ++i)
{
printf("Case %d:\n", i);
scanf("%d", &n);
Initialize();
for(int j=1; j<=n; ++j)
{
scanf("%d", &m);
int binary = 0, idx;
while(m--)
{
scanf("%d", &idx);
//每读入一个编号,更新一次状态
binary |= 1<<(idx-1);
}
//在线段树中更新此下标的状态
Update(1, 1, n, j, binary);
}
scanf("%d", &q);
while(q--)
{
scanf("%d %d", &l, &r);
int res = Query(1, 1, n, l, r);
int digit = 1;
bool is_first = true;
//遍历状态来输出存在的进化石编号
while(res)
{
//如果当前状态的最后一位为1,则输出
if(res & 1)
{
if(is_first)
{
is_first = false;
}
else
{
printf(" ");
}
printf("%d", digit);
}
//判断完一位,右移删掉最后一位
res >>= 1;
digit++;
}
if(digit == 1)
{
printf("%%");
}
printf("\n");
}
}
return 0;
}
void Initialize()
{
memset(st, 0, sizeof(st));
}
void Update(int node, int l, int r, int index, int value)
{
int mid = (l + r) >> 1;
if(l == r)
{
st[node] |= value;
return;
}
if(index <= mid)
{
Update(node<<1, l, mid, index, value);
}
else
{
Update(node<<1|1, mid+1, r, index, value);
}
//更新为左右子结点的合并状态
st[node] = st[node<<1] | st[node<<1|1];
}
int Query(int node, int l, int r, int il, int ir)
{
int mid = (l + r) >> 1;
if(l == il && r == ir)
{
return st[node];
}
if(ir <= mid)
{
return Query(node<<1, l, mid, il, ir);
}
else if(il > mid)
{
return Query(node<<1|1, mid+1, r, il, ir);
}
else
{
//跨区间查询时,返回左右两边状态的合并状态
return Query(node<<1, l, mid, il, mid)
| Query(node<<1|1, mid+1, r, mid+1, ir);
}
}