博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[JZOJ6359] 【NOIP2019模拟2019.9.15】小ω的树
阅读量:5292 次
发布时间:2019-06-14

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

题目大意

给你一棵树,带点权和边权。

要你选择一个联通子图,使得点权和乘最小边权最大。
支持修改点权操作。


思考历程

显然,最先想到的当然是重构树了……

重构树就是在做最大生成树的时候,当两个联通块相连时,新增一个点,将两个联通块的根节点连上去。
这个新建的点上记录这条边的边权,那么以它为子树的答案就是子树的点权和乘上自己表示的这条边的边权。

然后题目就变成了一个似乎很经典的问题:给你\(a_i\)\(b_i\),每次修改可以将区间内的\(a_i\)区间加,询问最大的\(a_ib_i\)……

然而由于我智力低下,完全不会维护啊……
于是就暴力维护了。


正解

其实可以分块……

这可以看做一堆一次函数取最大值。
如果只是一条链,直接分成\(\sqrt n\)块,每个块里维护一个凸壳。
每个块有个\(tag\)标记,表示当前块内的答案\(ans\)\(x=tag\)和块内的一次函数交点的纵坐标最大值。
那么修改的时候整块就直接改\(tag\),然后在凸壳上二分出\(ans\)。散块就暴力重构。

对于一棵树,当然是树链剖分了。设某条重链的长度为\(L\),则将重链分成\(\sqrt L\)块,然后一模一样地搞。

这样时间复杂度是否有保障呢?
首先,对于一条重链,它自己的时间复杂度就是\(\sqrt L \lg L\)
既然有好多条重链,是不是意味着再乘个\(\lg\)
然而不是这样。
树链剖分有个性质:轻儿子的大小小于整棵树大小的一半。
所以从上到下,每个重链的链顶子树的大小是一直在减半的。
设子树大小为\(S\)。显然\(\sqrt L \lg L\leq \sqrt S \lg S\)
所以时间复杂度就是\(\sqrt S \lg S+\sqrt \frac{S}{2} \lg \frac{S}{2}+\sqrt \frac{S}{4} \lg \frac{S}{4}+...\)
\(\lg\)比较小,所以看成同样的。提出来,就变成了\(\sqrt S+\sqrt \frac{S}{2}+\sqrt \frac{S}{4}+...=O(\sqrt S)\)
所以单次操作时间复杂度居然是\(O(\sqrt n\lg n)\)
所以这是能够过的……
不过记得要卡一卡常数。

另外有个强大的集训队大佬提供的方法:定期重构。

也就是做几个询问重构一次。
设做\(B\)个询问重构一次。
对于询问的点,建一棵虚树。显然虚树的点有\(2B-1\)个。建完之后对于虚树上的每一条边维护一个凸壳,记录一个\(tag\),求答案的时候在凸壳上二分。
计算询问的时候,直接暴力沿着边打\(tag\)。虚树的边有\(2B-2\)个,所以一次修改时间就是\(O(B \lg n)\)
所以总的时间就是:\(O(n\frac{m}{B}\lg n+mB\lg n)\)
平衡规划得\(B=\sqrt n\)


代码

树链剖分:

using namespace std;#include 
#include
#include
#include
#define N 300010#define ll long longinline int input(){ char ch=getchar(); while (ch<'0' || '9'
b.v;}int cnt,fa[N*2];int ufs[N*2];inline int get(int x){ if (ufs[x]==x) return x; return ufs[x]=get(ufs[x]);}struct EDGE{ int to; EDGE *las;} e[N*2];int ne;EDGE *last[N*2];ll sum[N*2],mn[N*2],p[N*2],q[N*2];int siz[N*2],hs[N*2],top[N*2],dfn[N*2],nowdfn,lis[N*2],len[N*2];void dfs1(int x){ siz[x]=1; for (EDGE *ei=last[x];ei;ei=ei->las){ dfs1(ei->to); sum[x]+=sum[ei->to]; siz[x]+=siz[ei->to]; if (siz[ei->to]>siz[hs[x]]) hs[x]=ei->to; }}void dfs2(int x,int t){ top[x]=t; len[top[x]]++; dfn[x]=++nowdfn; lis[nowdfn]=x; if (hs[x]) dfs2(hs[x],t); for (EDGE *ei=last[x];ei;ei=ei->las) if (ei->to!=hs[x]) dfs2(ei->to,ei->to);}int bel[N*2],nb,L[N*2],R[N*2];int st[N*2],*h[N*2],*t[N*2];ll tag[N*2];ll ans[N*2];ll h1[20000000],h2[20000000],nh1,nh2;inline void recover(int b){ for (int i=L[b];i<=R[b];++i) q[lis[i]]+=p[lis[i]]*tag[b]; tag[b]=0;}int tmp[N*2];inline bool cmpt(int a,int b){return p[a]
q[b];}inline bool ok(int a,int b,int x){return 0>=x*(p[a]-p[b])+q[a]-q[b];}inline void getans(int b){ int *l=h[b]+1,*r=t[b],*res=h[b]; while (l<=r){ int *mid=l+(r-l>>1); if (ok(*(mid-1),*mid,tag[b])) l=(res=mid)+1; else r=mid-1; } ans[b]=p[*res]*tag[b]+q[*res];}inline bool judge(int i,int j,int k){ return (q[k]-q[j])*(p[j]-p[i])>=(q[i]-q[j])*(p[j]-p[k]);}inline void build(int b){ int k=0; for (int i=L[b];i<=R[b];++i) tmp[k++]=lis[i]; sort(tmp,tmp+k,cmpt); *h[b]=tmp[0]; t[b]=h[b]; for (int i=1;i

总结

分块大法好啊……

转载于:https://www.cnblogs.com/jz-597/p/11536277.html

你可能感兴趣的文章
maven 依赖、聚合和继承 (转)
查看>>
selinux介绍/状态查看/开启/关闭
查看>>
DockerAPI版本不匹配的问题
查看>>
Leetcode: Ugly Number II
查看>>
项目立项管理
查看>>
(没时间维护,已下架)博客园第三方客户端-i博客园正式发布App Store
查看>>
map使用实例
查看>>
关于ShapeDrawable应用的一些介绍(上)
查看>>
洛谷 P3984 高兴的津津
查看>>
洛谷 P1308 统计单词数
查看>>
使用GitHub
查看>>
1.25回溯 n皇后问题,素数环,困难的串
查看>>
大量界面刷新时手动Dispose也是有必要的
查看>>
机电传动控制第三周学习笔记
查看>>
删除.gitignore中的在version control中的文件
查看>>
java精确计算、精确计算工具类
查看>>
操作系统实验零——操作系统实验环境准备
查看>>
centos服务器搭建javaweb项目步骤
查看>>
Docker入坑指南之EXEC
查看>>
XmlNode和XmlElement(转)
查看>>