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

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);
    }
}