Infinite Tree

题意:

题解:

参考博客
看了好一阵子才明白。。。emm。
我们先按照题意画出一部分树

我们先不考虑复杂度,这题应该怎么做?
题目给了每个点的权值w[i],问一个点到所有的节点路径长度*点权之和最小是多少,很明显是树形dp
dp[i]表示以i为根的子树到i的w和
sum[i]表示乘上距离之后的答案
dep[i]表示深度,now为当前节点,son为子节点
则有:

dp[now]= w[now]
sum[now]=0
dp[now]+=dp[son]
sum[now]+=sum[son]+(dep[son]-dep[now])*sum[son]

但是以now为根并不一定是最佳答案,所以我们还要不断的换根,求出最佳答案
如果将now为根转移到son为根,我们不用重新算一遍,只需要在之前的基础上

sum[now]-=sum[son]+(dep[son]-dep[now])*dp[son]
dp[now]-=dp[son]
sum[son]+=sum[now]+(dep[son]-dep[now])*dp[now]
dp[son]+=dp[now]

这样就OK了
但是!
题目是要求u到i!的距离,i!的增长速度很快,树的增长也是很快,如果全建出来肯定TLE了,所以我们没办法将整棵树建立,只能选择性建立,这要用到虚树
我们将阶乘点当做关键点,保留关键点及其lca,然后建立数就行
现在又有一个新问题,lca怎么计算?
我们首先考虑a!和(a+1)!的dfn谁大?
因为后者的因子一定包含前者的因子,所以除以mindiv后,(a+1)!的深度肯定更大,所以这些阶乘数的dfn是随a值从小到大的,这样我们只需要考虑相邻两个点的lca即可
我们列一个表格记录阶乘数分解后有多少个质数

我们根据上面的表格来列出相邻的LCA
2!和3 ! : 1,深度为1
3!和4 ! : 6,深度为3
4!和5 !: 1,深度为1
5!和6 !: 15,深度为3
6!和7 !: 1,深度为1
我们可以得出,对于a!和(a+1)!,LCA就是从大到小公共的质因子的乘积,遇到不同的就停止,深度是相同的个数+1
例如5!和6!:
5!分解后:5 3 2 2 2
6!:5 3 3 2 2 2 2
从大到小,一样的是5和3 ,(从第三位开始不一样,停止)
lca就是15,深度就是3

可以得到dep[lca((i+1)!,i!)] = sum(maxdiv(i+1),n)
sum(maxidv(i+1),n)为原本i!中大于等于maxdiv(i+1)的因子个数
这样我们就可以快速算出LCA
但是还是不够快,如果对于每个数都扫一次的话,还是很慢。所以,需要一个快速地查找求和,修改的算法,那就是用到了线段树或树状数组

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 1ll<<60
using namespace std;
const int MAXN=1e5+10;
int num[MAXN],w[MAXN<<1],d[MAXN<<1],stk[MAXN];
int ldfn[MAXN],rdfn[MAXN],dep[MAXN],lcad[MAXN],m;
int mndv[MAXN],pcnt=0;
ll dp1[MAXN<<1],dp2[MAXN<<1],ans;
vector<int> vir[MAXN<<1];
ll tr[MAXN<<2];//注意开long long
void Build(int root,int l,int r)
{tr[root]=0;if(l==r) return;int mid=l+r>>1;Build(root<<1,l,mid);Build(root<<1|1,mid+1,r);
}
void Change(int root,int l,int r,int x)
{if(l==r){tr[root]++;return;}int mid=l+r>>1;if(x<=mid) Change(root<<1,l,mid,x);else Change(root<<1|1,mid+1,r,x);tr[root]=tr[root<<1]+tr[root<<1|1];
}//x是插入的质数,要将其计数+1
int Query(int root,int x,int l,int r)
{              if(l>=x) return tr[root];int mid=l+r>>1;if(x<=mid) return Query(root<<1,x,l,mid)+Query(root<<1|1,x,mid+1,r);else if(x>mid) return Query(root<<1|1,x,mid+1,r);
}//查找当前质数的个数
void build()
{//^不等于,相当于!=dep[1]=1;for(int i=2;i<=m;i++){dep[i]=dep[i-1];int j=i;while(j^mndv[j]) j/=mndv[j];lcad[i]=Query(1,j,1,m)+1;for(j=i;j^1;dep[i]++,j/=mndv[j]) Change(1,1,m,mndv[j]);}int top=0,tot=m;stk[++top]=1;for(int i=2;i<=m;i++){if(top==1||lcad[i]==dep[stk[top]]){stk[++top]=i;continue;}while(top>1&&lcad[i]<=dep[stk[top-1]]){vir[stk[top-1]].push_back(stk[top]);top--;}//建虚树的基本操作,不会的建议去学习一下if(lcad[i]^dep[stk[top]]){dep[++tot]=lcad[i];w[tot]=0;vir[tot].push_back(stk[top]);stk[top]=tot;}stk[++top]=i;}while(top>1){vir[stk[top-1]].push_back(stk[top]);top--;}
}//原理同上,供参考
void dfs1(int x,int fa)
{dp1[x]=w[x];dp2[x]=0;for(int i=0;i<vir[x].size();i++){int son=vir[x][i];if(son==fa) continue;dfs1(son,x);dp1[x]+=dp1[son];dp2[x]+=dp2[son]+(dep[son]-dep[x])*dp1[son];}
}
void dfs2(int x,int fa)
{ans=min(ans,dp2[x]);for(int i=0;i<vir[x].size();i++){int son=vir[x][i];if(son==fa) continue;ll x1=dp1[x],x2=dp2[x],son1=dp1[son],son2=dp2[son];dp2[x]-=dp2[son]+(dep[son]-dep[x])*dp1[son];dp1[x]-=dp1[son];dp2[son]+=dp2[x]+(dep[son]-dep[x])*dp1[x];dp1[son]+=dp1[x];dfs2(son,x);dp1[x]=x1,dp2[x]=x2,dp1[son]=son1,dp2[son]=son2;}
}//树形dp+换根,非重点,且是模板一套的问题,上面已经分析
int main()
{mndv[1]=1;for(int i=2;i<MAXN;i++)if(!mndv[i])for(int j=i;j<MAXN;j+=i)if(!mndv[j]) mndv[j]=i;//预处理出每个数的mindiv,之后分解时可以用while(~scanf("%d",&m)){for(int i=1;i<=m;i++)scanf("%d",&w[i]);for(int i=0;i<=m*2;i++){vir[i].clear();dp1[i]=dp2[i]=0;}Build(1,1,m);build();dfs1(1,0);ans=dp2[1];dfs2(1,0);printf("%lld\n",ans);}
}
#include<bits/stdc++.h>
#define lowbit(x) x&-x
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 10;
//建立虚树点数tot < 2n, 空间开两倍int n, w[MAX];
ll ans;//树状数组
int c[MAX];
void upd(int p, int k) { for (; p <= n; p += lowbit(p)) c[p] += k; }
int query(int p) { int res = 0; for (; p; p -= lowbit(p)) res += c[p]; return res; }int mindiv[MAX];
void sieve(int siz) {//筛mindivfor (int i = 2; i <= siz; i++)if (!mindiv[i])for (int j = i; j <= siz; j += i)if (!mindiv[j])mindiv[j] = i;
}int lcadep[MAX], dep[MAX];
int st[MAX], top, tot;//stack, top, tot:虚树点数
vector<int> g[MAX];//虚树
void add_edge(int u, int v) { g[u].push_back(v), g[v].push_back(u); }void buildVirtualTree() {tot = n;st[top = 1] = 1;for (int i = 2; i <= n; i++) {dep[i] = dep[i - 1] + 1; int j = i;for (; j != mindiv[j]; j /= mindiv[j]) dep[i]++;lcadep[i] = query(n) - query(j - 1);for (j = i; j != 1; j /= mindiv[j]) upd(mindiv[j], 1);}//建树for (int i = 2; i <= n; i++) {while (top > 1 && dep[st[top - 1]] >= lcadep[i])add_edge(st[top - 1], st[top]), top--;if (dep[st[top]] != lcadep[i]) {dep[++tot] = lcadep[i];add_edge(tot, st[top]);st[top] = tot;}st[++top] = i;}while (top > 1){add_edge(st[top - 1], st[top]);top--;}
}void dfs(int u, int fa) {ans += 1ll * w[u] * dep[u];//ans最开始是rt = 1时的答案for (auto &v: g[u])if (v != fa) {dfs(v, u);w[u] += w[v];}
}void dfs2(int u, int fa) {//如果rt移动之后答案变小就一直移动下去,直到答案不在变小for (auto &v: g[u])if (v != fa) {//rt从u转移到v的代价//+(w[1] - w[v]) - w[v]if (w[1] - 2 * w[v] < 0) {ans += 1ll * (w[1] - 2 * w[v]) * (dep[v] - dep[u]);//一步的代价*距离dfs2(v, u);}}
}void init() {ans = top = 0;for (int i = 1; i <= tot; i++) {g[i].clear();c[i] = w[i] = lcadep[i] = dep[i] = 0;}
}int main() {sieve(1e5);while (~scanf("%d", &n)) {init();for (int i = 1; i <= n; i++) scanf("%d", &w[i]);buildVirtualTree();int rt = 1;dfs(rt, 0);dfs2(rt, 0);printf("%lld\n", ans);}return 0;
}

Infinite Tree相关推荐

  1. 牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)

    题目链接:点击查看 题目大意:给出一个无穷个节点的树,对于每个大于 1 的点 i 来说,可以向点 i / minvid[ i ] 连边,这里的 mindiv[ x ] 表示的是 x 的最小质因数,现在 ...

  2. HDU 5573 Binary Tree 构造

    Binary Tree 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5573 Description The Old Frog King lives ...

  3. 2020牛客暑期多校训练营(第一场)

    文章目录 A B-Suffix Array B Infinite Tree C Domino D Quadratic Form E Counting Spanning Trees F Infinite ...

  4. 2020 牛客多校第一场

    2020 牛客多校第一场 A. B-Suffix Array 后缀数组的思想:倍增+桶排序的方式找出一串连续序列后缀的大小.虽说正常使用的时候都是字典序,但是只要修改排序方式,也能够达到一个类似的&q ...

  5. elementUI vue页面代码

    <img src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg" alt= ...

  6. k-d tree树 近邻算法

    k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构.主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索). 应用背景 SIFT算法中做特征点匹配的时候就会利用到k ...

  7. k-d tree算法

    k-d树(k-dimensional树的简称),是一种分割k维数据空间的数据结构.主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索). 应用背景 SIFT算法中做特征点匹配的时候就会利用到k ...

  8. 深入Flutter(四) Infinite scrolling -- 无限滚动

    文章系列 深入Flutter(一) 积极推进"组合"方式 深入Flutter(二) 线性时间复杂度的 build 和 layout 深入Flutter(三) Element树和Re ...

  9. 使用element-ui中tree树状图

    效果图 1.通过slot-scope插槽来自定义样式 :icon-class="'1'"这里主要是为了把icon给冲突掉不显示 随便赋个值就可以 <el-tree:data= ...

最新文章

  1. css如何做玻璃效果_拓展训练之后的效果保持工作该如何做?
  2. 《麻省理工科技评论》发布2019年全球十大突破性技术!
  3. 市面上有没有靠谱的PM2.5检测仪?如何自己动手制作PM2.5检测仪
  4. Java爬虫——B站弹幕爬取
  5. stm32 堆和栈(stm32 Heap Stack)【worldsing笔记】
  6. 【CF#757A】Gotta Catch Em' All!
  7. 中国地质大学计算机地理信息学院,英文主页 软件工程、计算机科学与技术、地理信息科学等相关专业学生 中国地质大学(武汉)教师个人主页系统...
  8. php 后期发展,php – 通过后期处理上传进度
  9. mysql倒叙varchar类型的数字_MySql遇到varchar字段数字字符串排序问题
  10. ORACLE 归档日志打开与关闭
  11. 初一的学生,上了初中成绩就下降了一大截,该怎么办?
  12. 网创第一件事是要找一起奋斗的人
  13. angualr Http error处理
  14. 漫画:什么是字符集和编码?ASCII、UTF-8、UTF-16、UTF-32 又是什么?
  15. 5G来临,一对一直播社交源码如何利用热门机制吸引万千用户?
  16. 更改VS2010,VS2008,VS2012等指定默认浏览器操作方式
  17. 6U VPX板卡资料:6U VPX 高性能计算存储板卡
  18. 蓝桥ROS机器人之CoppeliaSim_Edu_V4_2_0
  19. 从零起步,新时代大学生选择创业还是工作?
  20. 华为如何开启找回服务器,华为服务器BIOS恢复出厂设置

热门文章

  1. 绝不能放进微波炉的10样东西,最后一个太意外
  2. python turtle 绘图_谈一下Pycharm中关联系统Python解释器的方法
  3. c语言字符比较思路,C语言讲解思路资料
  4. typora插入代码设置_Typora基本功能介绍
  5. java 手机 上传图片_在手机端使用拍照功能上传图片的功能的解决文案
  6. n以内的素数c语言,关于求N以内素数的一点小问题(N小于一亿)
  7. pdf编辑软件adobe acrobat_分享一款PDF编辑和阅读软件Acrobat
  8. pearson相关系数_Pearson(皮尔逊)相关系数
  9. 算法题目——第K大的数
  10. mysql innodb log_教你如何理解mysql中的innoDB log