青云的机房组网方案(简单+普通+困难)(虚树+树形DP+容斥)
题目链接
1.对于简单的版本n<=500, ai<=50
直接暴力枚举两个点x,y,dfs求x与y的距离。
2.对于普通难度n<=10000,ai<=500
普通难度解法挺多
第一种,树形dp+LCA
比赛的时候,我猜测对于不为1的n个数,其中两两互质的对数不会很多,肯定达不到n^2
然后找出所有互质的对数,然后对为1的数进行特殊处理。(初略的估计了下,小于500的大概有50个质数,将n个数平均分到这些数中,最后大概有10000*50*200=10^7)
对所有的非1质数对,采用离线LCA可以搞定。
对于1的特殊情况,只需要用两次dfs,就可以找出所有1到其它点的距离和与1之间的距离和。
第二种,树形dp+容斥
这种方法从边的角度,考虑每一条边会被计算多少次,这也是树上求距离的常用方法。
由于树边都是桥边,所有只要求出边两边联通块之间互质对数。最简单的想法即是枚举每一条边,然后再分别枚举两边区域,这样的复杂度是500*500*10000 很遗憾并没有这么简单。于是用容斥原理进行优化。在枚举某条边的一边的x(1<=x<=500)的时候,考虑右边为x质因子倍数的情况,也就是容斥了。 这样可以将复杂度变为10000*500*k*2^k( k<=4)
官方题解:
附上代码:
// // main.cpp // 160701 // // Created by New_Life on 16/7/1. // Copyright © 2016年 chenhuan001. All rights reserved. // #include <iostream> #include <stdio.h> #include <string.h> #include <vector> #include <algorithm> using namespace std; #define N 10100vector<int> save[505]; int g[N]; struct node {int to,next; }edge[2*N];int cnt,pre[N]; int dp[N][505]; int savecnt[N][505]; int cntall[505]; long long ans;void add_edge(int u,int v) {edge[cnt].to = v;edge[cnt].next = pre[u];pre[u] = cnt++; }void dfs(int s,int fa) {for(int p=pre[s];p!=-1;p=edge[p].next){int v = edge[p].to;if(v == fa) continue;dfs(v,s);for(int i=1;i<=500;i++){dp[s][i] += dp[v][i];savecnt[s][i] += savecnt[v][i];}}savecnt[s][ g[s] ]++;for(int i=0;i<(1<<save[g[s]].size());i++){int tmp = 1;for(int j=0;j<save[g[s]].size();j++){if(((1<<j)&i) != 0){tmp *= save[g[s]][j];}}dp[s][tmp]++;}//int last[505];int lastcnt[505];for(int p=pre[s];p!=-1;p=edge[p].next){int v = edge[p].to;if(v == fa) continue;for(int i=1;i<=500;i++){//last[i] = all[i]-dp[v][i];lastcnt[i] = cntall[i]-savecnt[v][i];}//对这条边进行处理for(int i=1;i<=500;i++){if(lastcnt[i] == 0) continue;for(int j=0;j<(1<<save[i].size());j++){int tcnt=0;int tnum = 1;for(int k=0;k<save[i].size();k++){if( ((1<<k)&j)!=0 ){tcnt++;tnum *= save[i][k];}}if(tcnt%2 == 0) ans += lastcnt[i]*dp[v][tnum];else ans -= lastcnt[i]*dp[v][tnum];}}}}int main(int argc, const char * argv[]) {for(int i=1;i<=500;i++){int ti = i;for(int j=2;j*j<=ti;j++){if(ti%j == 0){save[i].push_back(j);while(ti%j==0) ti/=j;}}if(ti != 1) save[i].push_back(ti);}//for(int i=1;i<=500;i++) printf("%d\n",save[i].size());cnt = 0;memset(pre,-1,sizeof(pre));int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",g+i);//得把每一项都变成最简单int tg =1;for(int j=0;j<save[ g[i] ].size();j++){tg *= save[ g[i] ][j];}g[i] = tg;}for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);add_edge(a,b);add_edge(b,a);}ans = 0;for(int ii=1;ii<=n;ii++){cntall[ g[ii] ]++;}dfs(1,-1);cout<<ans<<endl;return 0; }
3. 对于困难难度
这就需要用到虚树这种没听过的东西了,百度学习下,然后发现原理还是很简单的。应用情景,对于一颗树,挺大,但是需要操作的结点不多,这时候把需要操作的结点重新建一颗小的树(需要用的信息不能少)。
思路概述:(抄了下)
- 枚举 因数x,x是每种质因子至多有一个的数,记录一下x有几种质因子,方便之后容斥。
- 把所有x的倍数的权值的点找出来,预处理下可以做到找出来的点的dfs序是从小到大的,预处理也可以使得每次找x的倍数的权值的点不必线性扫一遍。
- 然后对这些点 O(n) 建虚树,具体操作是相邻两个点加进去 lca,用一个栈维护下父亲链即可。[bzoj3572]是一道典型的虚树的题目。
- 构建好树后在树上 dfs 两次可以求出所有x的倍数的权值的点对之间的距离和,就是第一遍dfs记录以节点u为根的子树中,有多少个x倍数的点(可能有一些是虚树添加进来的lca点),第二遍dfs其实是枚举每条边,计算(u,v)这条边的总价值,就是它出现的次数乘以它的权值;它出现的次数就是它子树中x倍数的点的个数,乘以不在它子树中x倍数的点的个数。
- 最后容斥下就可以求出答案。
由于所有步骤均是线性的,而所有虚树加起来的总点数也是线性乘上一个常数的,所以复杂度为 O(nK),K<=128。
对于复杂度分析,我抱有不同的看法,上述过程中建虚树是O(nlog(n))的,100000以内不重复质数最多是6个,所以最大复杂度为O(64*n*log(n))
// // main.cpp // Xushu // // Created by New_Life on 16/7/1. // Copyright © 2016年 chenhuan001. All rights reserved. // #include <iostream> #include <stdio.h> #include <string.h> #include <vector> #include <algorithm> using namespace std;#define N 100100 #define LN 20struct node {int to,next; }edge[2*N];int cnt,pre[N];void add_edge(int u,int v) {edge[cnt].to = v;edge[cnt].next = pre[u];pre[u] = cnt++; }int deep[N]; int g[N];//记录每个点的权值 vector<int>saveall[N];//记录i所有的倍数 int sign[N]; int len[N];//每个点到根的距离 int mark[N];//标示虚树上的点是否是无用点struct Lca_Online {int _n;int dp[N][LN];void _dfs(int s,int fa,int dd){int factor[30];int fcnt=0;int tmp = g[s];for(int i=2;i*i<=tmp;i++){if(tmp%i == 0){factor[ fcnt++ ] = i;while(tmp%i == 0) tmp/=i;}}if(tmp != 1) factor[ fcnt++ ] = tmp;for(int i=0;i<(1<<fcnt);i++){tmp = 1;int tsign = 1;for(int j=0;j<fcnt;j++)if( ((1<<j)&i) != 0 ){tmp *= factor[j];tsign *= -1;}saveall[tmp].push_back(s);sign[tmp] = tsign;}deep[s] = dd;for(int p=pre[s];p!=-1;p=edge[p].next){int v = edge[p].to;if(v == fa) continue;_dfs(v,s,dd+1);dp[v][0] = s;}}void _init(){for(int j=1;(1<<j)<=_n;j++){for(int i=1;i<=_n;i++){if(dp[i][j-1]!=-1) dp[i][j] = dp[ dp[i][j-1] ][j-1];}}}void lca_init(int n){_n = n;memset(dp,-1,sizeof(dp));//_dfs(firstid,-1,0);_dfs(1,-1,0);_init();}int lca_query(int a,int b){if(deep[a]>deep[b]) swap(a,b);//调整b到a的同一高度for(int i=LN-1;deep[b]>deep[a];i--)if(deep[b]-(1<<i) >= deep[a]) b = dp[b][i];if(a == b) return a;for(int i=LN-1;i>=0;i--){if(dp[a][i]!=dp[b][i]) a = dp[a][i],b = dp[b][i];}return dp[a][0];} }lca;int stk[N],top; vector<int>tree[N];//存边 vector<int>treew[N];//存权void tree_add(int u,int v,int w) {tree[u].push_back(v);tree[v].push_back(u);treew[u].push_back(w);treew[v].push_back(w); }long long down[N]; long long ccnt[N]; long long sum[N]; int nn;void dfs1(int s,int fa) {down[s] = 0;ccnt[s] = 0;for(int i=0;i<tree[s].size();i++){int to = tree[s][i];if(to == fa) continue;dfs1(to,s);down[s] += down[to] + ccnt[to]*treew[s][i];ccnt[s] += ccnt[to];}if(mark[s]==1)ccnt[s]++; }void dfs2(int s,int fa,long long num,long long tcnt) {sum[s] = down[s]+num+tcnt;for(int i=0;i<tree[s].size();i++){int to = tree[s][i];if(to == fa) continue;dfs2(to,s,sum[s]-down[to]-ccnt[to]*treew[s][i],(nn-ccnt[to])*treew[s][i]);} }int main(int argc, const char * argv[]) {cnt = 0;memset(pre,-1,sizeof(pre));int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",g+i);}for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);add_edge(a, b);add_edge(b, a);}lca.lca_init(n);long long ans=0;for(int x=1;x<=100000;x++){if(saveall[x].size() == 0) continue;//build virtual treetop = 0;stk[top++] = saveall[x][0];tree[ saveall[x][0] ].clear();treew[ saveall[x][0] ].clear();mark[saveall[x][0]]=1;for(int i=1;i<saveall[x].size();i++){int v = saveall[x][i];int plca = lca.lca_query(stk[top-1], v);//最近公共祖先if(plca == stk[top-1]) ;//不处理else{int pos=top-1;while(pos>=0 && deep[ stk[pos] ]>deep[plca])pos--;pos++;for(int j=pos;j<top-1;j++){tree_add(stk[j],stk[j+1],deep[stk[j+1]]-deep[stk[j]]);}int prepos = stk[pos];if(pos == 0){tree[plca].clear(),treew[plca].clear(),stk[0]=plca,top=1;mark[plca] = 0;}else if(stk[pos-1] != plca){tree[plca].clear(),treew[plca].clear(),stk[pos]=plca,top=pos+1;mark[plca] = 0;}else top = pos;tree_add(prepos,plca,deep[prepos]-deep[plca]);}tree[v].clear();treew[v].clear();stk[top++] = v;mark[v] = 1;}for(int i=0;i<top-1;i++){tree_add(stk[i], stk[i+1], deep[stk[i+1]]-deep[stk[i]]);}//构建好了虚树,然后就是两次dfsnn = (int)saveall[x].size();dfs1(saveall[x][0],-1);dfs2(saveall[x][0],-1,0,0);long long tans=0;for(int i=0;i<saveall[x].size();i++)tans += sum[ saveall[x][i] ];tans /= 2;ans += sign[x]*tans;}cout<<ans<<endl;//时间,内存。return 0; }
青云的机房组网方案(简单+普通+困难)(虚树+树形DP+容斥)相关推荐
- 图解通信原理与案例分析-23:5G NR的三大场景、四大基本特征、三组关键技术、两大网络架构演进、八大组网方案
目录 第一章 移动通信发展路径 第二章 5G三大应用场景(外在服务) 2.1 eMBB 增强移动宽带 2.2 URLLC 超可靠低时延通信 2.3 mMTC 海量机器类通信 第三章 5G的四大特 ...
- amf组网_【5G核心网】5G核心网SA组网方案及4G/5G互操作探讨
摘 要: SA(Standalone 独立组网)架构是5G核心网的目标架构.在SA架构下,4G/5G互操作是在核心网实现.本文针对4G/5G核心网互操作,分析了控制面网元整体部署架构.接入层网元组网 ...
- WayOS路由智能小区组网方案
随着我国城市基础建设的发展,房地产业持续增温,越来越多的住宅小区开始在城市中出现.而且小区的建设也越来越复杂和庞大,包括住宅.商店.学校.停车场.商务会所等各种与生活相关的设施全都在一个社区中出现,这 ...
- 一文了解GaussDB 200的产品架构、数据流程、组网方案、服务部署原则、企业级增强特性......
导读:本文是对GaussDB 200产品的整体描述,包含产品架构.数据流程.组网方案.服务部署原则.企业级增强特性等. 产品描述 GaussDB 200是企业级的大规模并行处理关系型数据库.Gauss ...
- 针对平层住宅的分布式无线组网方案(含万兆NAS)
往期文章:针对三层别墅的两种无线组网方案 java程序员MacOS系统下开发环境从"0"到"1" 上期文章介绍了针对三层别墅的两种无线组网方案,有朋友反馈要让写 ...
- 433模块 防冲撞 解决多发一收 mesh自组网 方案实现
433模块 防冲撞 解决多发一收 mesh自组网 方案实现 本文推荐一种自带组网协议,可实现自组网.串口透传.支持点对点星型mesh组网.防冲撞.数据加密.一键入网等功能的433模块,可代替zigbe ...
- IP RAN基站回传中的三大组网方案
IP RAN基站回传中的三大组网方案 随着3G业务的不断增长,用户对于高带宽的手机接入需求越来越多,对于回程网的承载需求呈现出了爆炸式的增长.一方面,客户需求与日俱增; 另一方面,设备的更新换代带来的 ...
- 安科瑞:列头柜、监控系统、触摸屏的数据中心机房配电方案
摘要 安科瑞精密配电系统是安科瑞针对数据中心集中监控要求提供的多回路监控装置,监控多回路电参量并可对各种故障进行告警.主要适用于各类列头柜.精密配电柜.电源分配柜.UPS输出柜等末端配电设备的监控.在 ...
- 基于GPRS、NB-IOT、LTE的自组网方案
一.概述 成都亿佰特电子科技有限公司是一家致力于自主研发和客户定制需求的创新型公司,成都亿佰特针对GPRS.NB-IOT.LTE推出了RS232.R485接口的E840-DTU系列数传电台. 针对目前 ...
最新文章
- 英文关键词计算tf-idf 余弦相似度_TFIDF原理与实践
- 成功解决ValueError: Expected 2D array, got 1D array instead: Reshape your data either using array.reshap
- ES6——举个例子理解Promise的原理和使用
- matlab里面filename1,matlab 每次从一个txt文件(里面每行是一个图像名字,如1.jpg之类的)读取一行字符串...
- Android 应用开发---6.ViewPager+Fragment的基本使用
- Spring -- 入门,装备集合,自动装配,分散装配,自定义编辑器
- 使用TensorFlow.js的AI聊天机器人一:检测文本中的情绪
- 如何组合来自多个SQL表的结果(提示:有三种方式)
- [集成IronPython] 使用Module扩展IronPython
- 官方 Material Design App
- jsweet下载编译
- 基于javaweb的校园自行车租赁管理系统(java+jsp+javascript+servlet+mysql)
- “磁碟机”病毒技术分析报告
- PAT 1058 选择题 python
- Android 关于佳博和汉印蓝牙热敏打印机开发
- APF有源电力滤波器Simulink仿真
- 通过调用Word模板(Doc、dot)直接打印 z
- 谷粒学院——后台管理系统功能模块
- Java中的代码点和代码单元(转)
- 语音质量检测 梅尔倒谱失真MCD
热门文章
- Letter Combinations of a Phone Number
- Spring Cloud 分布式链路跟踪 Sleuth + Zipkin + Elasticsearch
- GetCurrentTime(),GetLocalTime(),GetSystemTime()之间的区别
- nginx配置详解(转)
- ios中获取各种文件的目录路径方法
- MySQL的主从复制与读写分离技术实例(一)主从复制
- Flashcache
- openstack 报错
- 《 ATSS:Adaptive Training Sample Selection》
- python 读取一个文件夹下所有图像