计蒜客 Set (2018 ICPC亚洲区域赛网络赛 南京 H)(字典树合并)

版权声明:Why is everything so heavy? https://blog.csdn.net/lzc504603913/article/details/82319948

Shinku is very interested in the set. One day, she got nn sets, and the ii-th number a_iai​ is in the ii-th set. But she doesn't think it is interesting enough, so she applies mm magic to these sets. There are three kinds of magic:

1\ u\ v1 u v: If the uu-th and vv-th numbers are not in one set, then the Shinku's magic will merge the set containing the uu-th number and the set containing the vv-th number.

2\ u2 u: Shinku's magic adds 11 to each number in the set containing the uu-th number.

3\ u\ k\ x3 u k x: Shinku can immediately know how many numbers tt in the set containing the uu-th number satisfy t\equiv x (\bmod\ 2^k)(0 \le k\le 30,0\le x<2^k)t≡x(mod 2k)(0≤k≤30,0≤x<2k).

But unfortunately, for some reason the type 33 magic fails. So Shinku wants you to tell her the answer after every type 33 magic.

Note that there can be multiple numbers with the same value in one set, that is, numbers with the same value will not disappear when merged.

Input

The first line contains two integers n, m(1 \le n, m \le 6 \times 10^5)n,m(1≤n,m≤6×105), the number of initial sets and the number of the magic.

The second line contains nn integers. The ii-th number a_i(0 \le a_i \le 10^9)ai​(0≤ai​≤109) is the number in the ii-th set initially.

The next mm lines describe the sequence of magic. The ii-th line describes the ii-th magic. Each magic is a magic as described above.

Output

For each type 33 magic, output the answer you are asked to calculate.

Hint

After the first operation, the numbers are 2,3,42,3,4, sets are \lbrace 2,4 \rbrace \lbrace 3 \rbrace{2,4}{3}

For the second operation, the third number is in \lbrace 2,4 \rbrace, 2 \equiv 0\pmod {2^1}, 4 \equiv 0\pmod {2^1}{2,4},2≡0(mod21),4≡0(mod21), so the answer is 22.

After the third operation, the numbers are 2,4,42,4,4, sets are \lbrace 2,4 \rbrace \lbrace 4 \rbrace{2,4}{4}

After the forth operation, the numbers are 2,4,42,4,4, sets are \lbrace 2,4,4 \rbrace{2,4,4}

For the fifth operation, ,the third number is in \lbrace 2,4,4 \rbrace, 2 \equiv 0\pmod {2^1}, 4 \equiv 0\pmod {2^1}{2,4,4},2≡0(mod21),4≡0(mod21),
4 \equiv 0\pmod {2^1}4≡0(mod21), so the answer is 33.

样例输入复制

3 5
2 3 4
1 1 3
3 3 1 0
2 2
1 2 3
3 3 1 0

样例输出复制

2
3

题目来源

ACM-ICPC 2018 南京赛区网络预赛

题意:一开始每个集合里有一个数。要求支持操作:

1. 合并两个集合

2. 把一个集合里的数都 +1

3. 询问一个集合里满足 x≡a(mod2^k) 的 x 的个数。

解题思路:字典树合并。首先发现询问 3 就是相当于求把所有数字倒过来插入一个 trie 以后某个节点上的和。把一个 trie 上的数字 +1,相当于交换 0/1 然后在左子树(0) 下递归交换。集合合并直接启发式合并,用动态开点优化,防止爆内存,即使这样,也是刚好够内存…………

#include<iostream>
#include<string.h>
#include<queue>
#include<bitset>
#include<vector>
using namespace std;
typedef long long ll;
const int MAXN=600105;
const int INF=0x3f3f3f3f;

int ch[MAXN*71][2];
int num[MAXN*71];
int tot=0;
int root[MAXN];

int insert(int x){
    int nrt=++tot;
    num[nrt]=1;//注意k可能为0,所以根节点的值要初始化为1(WA了好久……)
    int res=nrt;
    bitset<32> s(x);
    for(int i=0;i<=31;i++){
        ch[nrt][s[i]]=++tot;
        num[ch[nrt][s[i]]]++;
        nrt=ch[nrt][s[i]];
    }
    return res;
}

int merge(int r1,int r2){
    if(r1==0||r2==0)
        return r1+r2;
    int nrt=++tot;//记得新建节点
    num[nrt]=num[r1]+num[r2];//直接合并
    ch[nrt][0]=merge(ch[r1][0],ch[r2][0]);
    ch[nrt][1]=merge(ch[r1][1],ch[r2][1]);
    return nrt;
}

void rev(int u){
    if(!u)return;
    swap(ch[u][0],ch[u][1]);
    rev(ch[u][0]);
}

int query(int x,int k,int rt){
    bitset<32> s(x);
    int ans=num[rt];//注意k可能为0
    int nrt=rt;
    for(int i=0;i<k;i++){
        ans=num[ch[nrt][s[i]]];
        nrt=ch[nrt][s[i]];
    }
    return ans;
}

int fa[MAXN];
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

int a[MAXN];

int main(){

    int N,M;
    scanf("%d%d",&N,&M);
    tot=0;
    for(int i=1;i<=N;i++)
    {
        scanf("%d",&a[i]);
        fa[i]=i;
        root[i]=insert(a[i]);
    }

    int op,u,v,k,x;
    while(M--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&u,&v);
            int fx=find(u);
            int fy=find(v);
            if(fx!=fy){
                fa[fx]=fy;
                root[fy]=merge(root[fx],root[fy]);
            }
        }
        if(op==2){
            scanf("%d",&u);
            rev(root[find(u)]);
        }
        if(op==3){
            scanf("%d%d%d",&u,&k,&x);
            printf("%d\n",query(x,k,root[find(u)]));
        }
    }

    return 0;
}


猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/82319948
今日推荐