ACM 2018 南京网络赛H题Set解题报告

题目描述

给定\(n\)个数$a_i$,起初第\(i\)个数在第\(i\)个集合。有三种操作(共\(m\)次):

1 $u$ $v$ 将第$u$个数和第$v$个数所在集合合并

2 $u$ 将第$u$个数所在集合所有数加1

3 $u$ $k$ $x$ 问$u$所在集合有多少个数模$2^k$余$x$。

数据范围:\(n,m \le 500000,a_i \le 10^9, 0 \le k \le 30\)。

简要题解

显然此题可以用set加启发式合并在\(O(n \log ^2 n)\)时间复杂度解决本题,但此题时限1.5s,必须使用一个log的做法。事实上,这是一道十分套路的Trie树题目。

首先用并查集维护连通性。

下面先考虑操作3,这相当于询问低$k$位二进制固定时集合中元素个数,可以用Trie树,维护一个子树中终结结点有多少个即可。

对于加1操作,可以在Trie树上打标记,类似线段树进行pushDown标记下传。此题pushDown函数很新颖(之前没写过这样的pushDown),详见代码。

对于合并操作,类似线段树合并。由于初始时\(n\)个数共需要\(O(n \log 10^9)\)个结点,而花费O(1)的时间会将总结点数减1,故Trie树合并的总时间复杂度也为\(O(n \log 10^9)\)。关于线段树合并,可以做这道入门题练手:Codeforces Gym 101194G(2016EC Final)

总时间复杂度\(O((n+q) \log 10^9)\)。

注意事项

此题空间复杂度\(O(n \log 10^9)\)。如果Trie树合并使用新开的结点,每个结构体16B,将需要576MB,这会MLE。考虑Trie树合并时不新开结点,可以将空间降至288MB。

完整代码

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<vector>
 4 #define DEPTH 30
 5 using namespace std;
 6 struct Trie{
 7     int size;
 8     int next[2];
 9     int tag;
10 }trie[20000001];
11 int cnt;
12 int newTrie(){
13     memset(&trie[++cnt], 0, sizeof(Trie));
14     return cnt;
15 }
16 inline void pushDown(int i){
17     int &t = trie[i].tag;
18     int &l = trie[i].next[0], &r = trie[i].next[1];
19     if (t){
20         if (t & 1){ swap(l, r); trie[l].tag++; }
21         if (t >= 2){ trie[l].tag += t / 2; trie[r].tag += t / 2; }
22         t = 0;
23     }
24 }
25 inline void pushUp(int i){
26     trie[i].size = trie[trie[i].next[0]].size + trie[trie[i].next[1]].size;
27 }
28 void insert(int i, int depth, int x)
29 {
30     if (!depth){ trie[i].size++; return; }
31     pushDown(i);
32     int &pos = trie[i].next[x & 1];
33     if (!pos)pos = newTrie();
34     insert(pos, depth - 1, x >> 1);
35     pushUp(i);
36 }
37 void merge(int& i, int j, int k, int depth)
38 {
39     if (j&&k){
40         i = j;
41         if (!depth){
42             trie[i].size += trie[k].size;
43             return;
44         }
45         pushDown(j); pushDown(k);
46         for (int c = 0; c < 2; c++)
47             merge(trie[i].next[c], trie[j].next[c], trie[k].next[c], depth - 1);
48         pushUp(i);
49     }
50     else i = j ? j : k;
51 }
52 int f[600001], id[600001];
53 int getFather(int i)
54 {
55     if (f[i] == i)return i;
56     return f[i] = getFather(f[i]);
57 }
58 int main()
59 {
60     int n, m, x, u, v, k;
61     scanf("%d%d", &n, &m);
62     for (int i = 1; i <= n; i++){
63         scanf("%d", &x);
64         id[i] = newTrie();
65         f[i] = i;
66         insert(id[i], DEPTH, x);
67     }
68     while (m--){
69         scanf("%d%d", &x, &u);
70         u = getFather(u);
71         if (x == 1){
72             scanf("%d", &v);
73             v = getFather(v);
74             if (u != v){
75                 f[u] = v;
76                 merge(id[v], id[u], id[v], DEPTH);
77             }
78         }
79         else if (x == 2)trie[id[u]].tag++;
80         else{
81             scanf("%d%d", &k, &x);
82             int cur;
83             for (cur = id[u]; k; k--){
84                 pushDown(cur);
85                 cur = trie[cur].next[x & 1];
86                 if (!cur)break;
87                 x >>= 1;
88             }
89             if (!cur)printf("0\n");
90             else printf("%d\n", trie[cur].size);
91         }
92     }
93 }

原文地址:https://www.cnblogs.com/zbh2047/p/9572187.html

时间: 2024-10-07 17:51:25

ACM 2018 南京网络赛H题Set解题报告的相关文章

ACM-ICPC 2018徐州网络赛-H题 Ryuji doesn&#39;t want to study

C*M....死于update的一个long long写成int了 心累 不想写过程了 ******** 树状数组,一个平的一个斜着的,怎么斜都行 题库链接:https://nanti.jisuanke.com/t/31460 #include <iostream> #include <cstring> #define ll long long #define lowbit(x) (x & -x) using namespace std; const int maxn =

ACM-ICPC 2018青岛网络赛-H题

把这题的每个点分成两种情况看,如果是从这个点开始,0算作2,1算作1,如果是中间点或者是结束点,如果和前面的相同看作2,不相同看作1 #include <iostream> #include <string> #include <string.h> using namespace std; int main() { ios::sync_with_stdio(false); int t; cin >> t; while (t--) { int a, b; st

2018徐州网络赛H. Ryuji doesn&#39;t want to study

题目链接: https://nanti.jisuanke.com/t/31458 题解: 建立两个树状数组,第一个是,a[1]*n+a[2]*(n-1)....+a[n]*1;第二个是正常的a[1],a[2],a[3]...a[n] #include "bits/stdc++.h" using namespace std; #define ll long long const int MAXN=1e5+10; ll sum[MAXN],ans[MAXN]; ll num[MAXN];

2019ICPC南京网络赛A题 The beautiful values of the palace(三维偏序)

2019ICPC南京网络赛A题 The beautiful values of the palace https://nanti.jisuanke.com/t/41298 Here is a square matrix of n * nn?n, each lattice has its value (nn must be odd), and the center value is n * nn?n. Its spiral decline along the center of the squar

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告

2016 第七届蓝桥杯 c/c++ B组省赛真题及解题报告 勘误1:第6题第4个 if最后一个条件粗心写错了,答案应为1580. 条件应为abs(a[3]-a[7])!=1,宝宝心理苦啊.!感谢zzh童鞋的提醒. 勘误2:第7题在推断连通的时候条件写错了,后两个if条件中是应该是<=12 落了一个等于号.正确答案应为116. 1.煤球数目 有一堆煤球.堆成三角棱锥形.详细: 第一层放1个, 第二层3个(排列成三角形), 第三层6个(排列成三角形), 第四层10个(排列成三角形). -. 假设一共

计蒜客 2018南京网络赛 I Skr ( 回文树 )

题目链接 题意 : 给出一个由数字组成的字符串.然后要你找出其所有本质不同的回文子串.然后将这些回文子串转化为整数后相加.问你最后的结果是多少.答案模 1e9+7 分析 : 应该可以算是回文树挺裸的题目吧 可惜网络赛的时候不会啊.看着马拉车想半天.卒... 对于每一个节点.记录其转化为整数之后的值 然后在回文串插入字符的时候 不断维护这个信息就行了 其实很好理解.看一下代码就懂了 ( 如果你学过回文树的话... ) 就是多加了变量 val .维护语句 #include<bits/stdc++.h

2018 徐州网络赛 H

Ryuji is not a good student, and he doesn't want to study. But there are n books he should learn, each book has its knowledge a[i]a[i]. Unfortunately, the longer he learns, the fewer he gets. That means, if he reads books from ll to rr, he will get a

2018南京网络赛L

题意是给一幅图可以把k条边权值变为0,求最短路. 分层图裸题,分层图忘完了啊,还在预流推进那做过几道题,难受. 分层图就是再开一维数组记录额外的状态,这道题可以把k条边权值变为0,那那一维数组j就表示有j条边权值为0,做个dp就好. #include <bits/stdc++.h> using namespace std; typedef long long ll; const int M = 1e5+7; const ll inf = 1e18; int _,n,m,k; int cnt,h

2018南京网络赛 - Skr 回文树

题意:求本质不同的回文串(大整数)的数字和 由回文树的性质可知贡献只在首次进入某个新节点时产生 那么只需由pos和len算出距离把左边右边删掉再算好base重复\(O(n)\)次即可 位移那段写的略微凌乱.. #include<bits/stdc++.h> #define rep(i,j,k) for(int i=j;i<=k;i++) #define rrep(i,j,k) for(int i=j;i>=k;i--) #define println(a) printf("