博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2018 ACM南京网络赛H题Set解题报告
阅读量:5301 次
发布时间:2019-06-14

本文共 3275 字,大约阅读时间需要 10 分钟。

题目描述

给定\(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),详见代码。(队友指出,此题也可以暴力更新Trie树,至多交换\(\log 10^9\)个结点;不过如果每次加的数不是1的话就必须打标记了。)

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

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

注意事项

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

完整代码

1 #include
2 #include
3 #include
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

你可能感兴趣的文章
20130330java基础学习笔记-语句_for循环嵌套练习2
查看>>
Spring面试题
查看>>
窥视SP2010--第一章节--SP2010开发者路线图
查看>>
MVC,MVP 和 MVVM 的图示,区别
查看>>
C语言栈的实现
查看>>
代码为什么需要重构
查看>>
TC SRM 593 DIV1 250
查看>>
SRM 628 DIV2
查看>>
2018-2019-2 20165314『网络对抗技术』Exp5:MSF基础应用
查看>>
Python-S9-Day127-Scrapy爬虫框架2
查看>>
SecureCRT的使用方法和技巧(详细使用教程)
查看>>
右侧导航栏(动态添加数据到list)
查看>>
81、iOS本地推送与远程推送详解
查看>>
虚拟DOM
查看>>
自建数据源(RSO2)、及数据源增强
查看>>
BootStrap2学习日记2--将固定布局换成响应式布局
查看>>
关于View控件中的Context选择
查看>>
2018icpc徐州OnlineA Hard to prepare
查看>>
Spark的启动进程详解
查看>>
使用命令创建数据库和表
查看>>