「51Nod 1601」完全图的最小生成树计数 「Trie」
题意
给定\(n\)个带权点,第\(i\)个点的权值为\(w_i\),任意两点间都有边,边权为两端点权的异或值,求最小生成树边权和,以及方案数\(\bmod 10^9 + 7\)
\(n \leq 10^5,W = max(w_i) \leq 2^{30}\)
题解
考虑按位贪心,我们从高到低考虑二进制第k位。每次把当前点集\(S\)分成第\(k\)位为\(0\)和第\(k\)位为\(1\)的两个集合,记为\(S_0, S_1\)。
我们递归下去把这两个集合连成生成树,然后再找一条最小的跨集合的边把这两个集合连通。
考虑这么做为啥对:假设有两条跨集合的边,我删去一条,树变成两个部分。然后任意找到一条集合内部边使集合\(S\)连通(既然有跨集合的边存在,我们一定能找到这样的一条边),这样显然更优。
然后考虑问题:找到\(x\in S_0,y\in S_1,x\text{ xor } y\)最小。
这个用类似线段树合并的方法:每次两个结点同时往下走,尽量往一边走。如果能同时往\(0/1\)走,都走一遍,复杂度是对的,每次合并复杂度是子树大小。考虑trie树上一个点只有\(O(\log W)\)个祖先,一共只有\(O(n \log W)\)个结点,所以复杂度\(O(n \log ^2 W)\)
我们再来考虑方案。叶子结点时假设大小为\(n\),也就是说\(n\)个点都是这个权值,生成树的方案数\(n^{n-2}\)(由prufer序列得)。非叶子结点时,方案是分成的两个集合的方案乘最后连边方案。连边会对应trie树上多对叶子\((u, v)\)(这些对结点异或起来都是最小的),若叶子\(u\)上放的数个数用\(cnt[u]\)表示,连边方案就是\(\sum_{(u,v)} cnt[u]*cnt[v]\)。
P.S.:快速幂写错了调了好久,差评
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
char gc() {
static char buf[1 << 20], * S, * T;
if(S == T) {
T = (S = buf) + fread(buf, 1, 1 << 20, stdin);
if(S == T) return EOF;
}
return *S ++;
}
template<typename T> void read(T &x) {
x = 0; char c = gc(); bool bo = 0;
for(; c > '9' || c < '0'; c = gc()) bo |= c == '-';
for(; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15);
if(bo) x = -x;
}
const int N = 1e5 + 10;
const int mo = 1e9 + 7;
int n, id = 1, ch[N * 30][2], cnt[N * 30], w[N * 30];
void insert(int x) {
int u = 1;
for(int i = 29; ~ i; i --) {
int y = x >> i & 1;
if(!ch[u][y]) {
ch[u][y] = ++ id;
w[id] = y << i;
}
u = ch[u][y];
}
cnt[u] ++;
}
ll ans;
int ans2, tot2, tot = 1;
int qpow(int a, int b) {
int ans = 1;
for(; b >= 1; b >>= 1, a = (ll) a * a % mo)
if(b & 1) ans = (ll) ans * a % mo;
return ans;
}
void merge(int u, int v, int now) {
now ^= w[u] ^ w[v];
if(cnt[u] && cnt[v]) {
if(now < ans2) { ans2 = now; tot2 = 0; }
if(now == ans2) tot2 = (tot2 + (ll) cnt[u] * cnt[v]) % mo;
return ;
}
bool tag = 0;
if(ch[u][0] && ch[v][0]) merge(ch[u][0], ch[v][0], now), tag = 1;
if(ch[u][1] && ch[v][1]) merge(ch[u][1], ch[v][1], now), tag = 1;
if(tag) return ;
if(ch[u][0] && ch[v][1]) merge(ch[u][0], ch[v][1], now);
if(ch[u][1] && ch[v][0]) merge(ch[u][1], ch[v][0], now);
}
bool solve(int u) {
if(!u) return 0;
if(cnt[u]) {
if(cnt[u] > 2) tot = (ll) tot * qpow(cnt[u], cnt[u] - 2) % mo;
return 1;
}
bool s = solve(ch[u][1]) & solve(ch[u][0]);
if(s) {
ans2 = 2e9 + 10; tot2 = 1;
merge(ch[u][0], ch[u][1], 0);
ans += ans2; tot = (ll) tot * tot2 % mo;
}
return 1;
}
int main() {
read(n);
for(int i = 1; i <= n; i ++) {
int x; read(x); insert(x);
}
solve(1);
printf("%lld\n%d\n", ans, tot);
return 0;
}
「51Nod 1601」完全图的最小生成树计数 「Trie」的更多相关文章
- 51Nod 1601 完全图的最小生成树计数
题目链接 分析: 这是一张完全图,并且边的权值是由点的权值$xor$得到的,所以我们考虑贪心的思想,考虑$kruskal$的过程选取最小的边把两个连通块合并,所以我们可以模仿$kruskal$的过程, ...
- 51Nod1601 完全图的最小生成树计数
传送门 我居然忘写题解啦!(记忆废) 不管怎么说,这题还算是一道好题啊……你觉得敦爷出的题会有水题么 …… 这题比较容易把人误导到Boruvka算法之类的东西上去(我们机房去刚D题的人一开始大多也被误 ...
- 51Nod1601 完全图的最小生成树计数 Trie Prufer编码
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1601.html 题目传送门 - 51Nod1601 题意 题解 首先我们考虑如何求答案. 我们将所有 ...
- 最小生成树计数 bzoj 1016
最小生成树计数 (1s 128M) award [问题描述] 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一 ...
- 树的Prufer 编码和最小生成树计数
Prufer数列 Prufer数列是无根树的一种数列.在组合数学中,Prufer数列由有一个对于顶点标过号的树转化来的数列,点数为n的树转化来的Prufer数列长度为n-2.它可以通过简单的迭代方 ...
- 【bzoj1016】 JSOI2008—最小生成树计数
http://www.lydsy.com/JudgeOnline/problem.php?id=1016 (题目链接) 题意 求图的最小生成树计数. Solution %了下题解,发现要写矩阵树,15 ...
- [BZOJ]1016 JSOI2008 最小生成树计数
最小生成树计数 题目描述 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同 ...
- bzoj1016 [JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3517 Solved: 1396[Submit][St ...
- 【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇
http://www.4gamer.net/games/216/G021678/20140714079/ 连载第2回的本回, Arc System Works开发的格斗游戏「GUILTY G ...
随机推荐
- 把axios挂载到vue实例上面/==Axios 各种请求方式传递参数格式
/*ajax请求*/ import axios from 'axios' axios.defaults.baseURL = 'https://api.douban.com/v2/movie' ...
- 无服务架构在IOT的应用场景——使用函数工作流处理DIS数据
在物联网领域,复杂性往往并非在于传感器,真正的复杂性在于各种传感器产生的大量数据,以及对这些数据的处理,所以开发者不得不花费大量的时间去构建和维护后端服务器来处理这样一个庞大的数据流.而在今天这个敏捷 ...
- CMake入门-03-还是HelloWorld
工作环境 系统:macOS Mojave 10.14.6 CMake: Version 3.15.0-rc4 Hello,World! 扩展-math 目录里的文件编译成静态库再由 main 函数调用 ...
- 搭建SSM环境(淘淘商城)
本文用到的资料: 链接:https://pan.baidu.com/s/1Pk_aI_PRbqRFP9i3o9Xodg 提取码:o4o4 1.1. 数据库 1.1.1. 使用navicat创建数据库连 ...
- 查找-------(HashCode)哈希表的原理
这段时间 在 准备软件设计师考试 目的是想复习一下 自己以前没怎么学的知识 在这个过程中 有了很大的收获 对以前不太懂得东西 在复习的过程中 有了很大程度的提高 比如在复习 程序 ...
- linux, kill掉占用60%多cpu的进程,几秒后换个pid 和 command 又出现
linux, kill掉占用60%多cpu的进程,几秒后换个pid 和 command 又出现?快速清理木马流程.假设木马的名字是xysbqaxjqy,如果top看不到,可以在/etc/init.d目 ...
- c# winfrom 子窗体分屏显示
参考博客:https://blog.csdn.net/kailan818/article/details/8517126 实现代码: private void button1_Click(object ...
- Python内置函数系列
Python内置(built-in)函数随着python解释器的运行而创建.在Python的程序中,你可以随时调用这些函数,不需要定义. 作用域相关(2) locals() :以字典类型返回当前位置 ...
- Java 面向对象(三)static 关键字
一.static 1.概述 static 的意思的静态的,也是一种修饰符. 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单属于某个对象的. 用 ...
- element-ui重置表单并清除校验的方法
this.$refs['activityForm'].resetFields(); 只会重置之前表单的内容,并不会清空 只需在关闭弹框的cancel方法中写上重置表单的方法即可 cancel() { ...