bzoj3435 [Wc2014]紫荆花之恋(动态点分治+替罪羊树)
题解
我终终终终终终于做出来啦!!!
作为一个没有学过替罪羊树的蒟蒻现场学了一下替罪羊树,作为一个平衡树都写数组版本的看着大佬的指针题解无语只能硬去理解然后照着抄了一波指针
然后怎么做呢?
先把题设式子变形一下$$dist(i,j)\leq r_i+r_j$$
$$dist(i,LCA)+dist(LCA,j)\leq r_i+r_j$$
$$r_i-dist(i,LCA)\geq dist(j,LCA)-r_j$$
然后我们在每一个点开两棵平衡树,分别维护以$i$为根的子树中$dist(i,u)-r_u$和$dist(fa[i],u)-r_u$的值。然后每一次跳点分树时,记录$r_i-dist(i,LCA)+1$,在平衡树里查询有多少个数小于它就好了,修改直接往上跳,不断改
然而如果原树是一条链怎么办?强制在线,必然会被卡成$O(n^2)$,怎么办?
我们联想一下替罪羊树的思想,如果点分树上某一个点的子树过大,直接拍扁重建。联想一下替罪羊树,可以发现时间复杂度是能得到保证的,这样可以保证时间复杂度是$O(nlogn)$。
因为蒟蒻是第一次写替罪羊树&&第一次码这么长的代码,于是加了一堆注释,米娜应该能够看懂吧……
// luogu-judger-enable-o2
//minamoto
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#define ll long long
#define inf 1000000000
#define N 100005
#define alpha 0.755
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(ll x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
int n,e,head[N],Next[N<<],ver[N<<],val[N];
inline void add(int u,int v){
//加边,构建原树
ver[++e]=v,Next[e]=head[u],head[u]=e;
ver[++e]=u,Next[e]=head[v],head[v]=e;
}
vector<int> to[N];
int f[N][],bin[],tp,dep[N],len[N];
inline int LCA(int a,int b){
if(dep[a]<dep[b]) a^=b^=a^=b;
int i,cha=dep[a]-dep[b];
for(i=tp;~i;--i) if(cha&bin[i]) a=f[a][i];
if(a==b) return a;
for(i=tp;~i;--i) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
return f[a][];
}
inline int dis(int a,int b){return len[a]+len[b]-(len[LCA(a,b)]<<);}
struct Goat{
int val,sz;Goat *ch[];
Goat(){}
inline bool bad(){
//判断是否某个子树过大
return ch[]->sz>=sz*alpha+||ch[]->sz>=sz*alpha+;
}
}*tree1[N],*tree2[N],mem[N<<],*pool[N<<],*null,*sta[N];
int tot,top;
void init(){
//构建内存池,避免动态开点时间复杂度太大
null=new Goat();
null->ch[]=null->ch[]=null,null->val=null->sz=;
for(int i=;i<(N<<);++i) pool[i]=mem+i;
tot=(N<<)-;
for(int i=;i<=n;++i) tree1[i]=tree2[i]=null;
}
Goat** insert(Goat *&a,int val){
//插入节点,并判断是否有子树过大
//注意要开引用
if(a==null){
a=pool[tot--],a->ch[]=a->ch[]=null;
a->val=val,a->sz=;return &null;
}
++a->sz;
//小于等于往左插,大于往右插
Goat **o=insert(a->ch[a->val<val],val);
if(a->bad()) o=&a;return o;
}
int getrk(Goat *o,int val){
//查找有多少比val小的数
if(o==null) return ;
return (o->val>=val)?getrk(o->ch[],val):(getrk(o->ch[],val)+o->ch[]->sz+);
}
void Erholung(Goat *o){
//清除节点,回收内存池
if(o==null) return;
if(o->ch[]!=null) Erholung(o->ch[]);
pool[++tot]=o;
if(o->ch[]!=null) Erholung(o->ch[]);
}
void travel(Goat *o){
//暴力重构整棵树(递归找节点)
if(o==null) return;
if(o->ch[]!=null) travel(o->ch[]);
sta[++top]=o;
if(o->ch[]!=null) travel(o->ch[]);
}
Goat* build(int l,int r){
//重构
if(l>r) return null;
int mid=l+r>>;
Goat *o=sta[mid];o->sz=r-l+;
o->ch[]=build(l,mid-),o->ch[]=build(mid+,r);
return o;
}
inline void rebuild(Goat *&o){top=,travel(o),o=build(,top);};
inline void Insert(Goat *&a,int val){
//同,这里和上面rebuild也要开引用
Goat **o=insert(a,val);
if(*o!=null) rebuild(*o);
}
int sz[N],son[N],size,rt,fa[N];bool vis[N];
void findrt(int u,int fa){
sz[u]=,son[u]=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=fa&&!vis[v]){
findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
}
}
cmax(son[u],size-sz[u]);
if(son[u]<son[rt]) rt=u;
}
void dfs(int u,int f,int rt){
//遍历子树,把所有的东西都插到平衡树里
Insert(tree1[rt],dis(u,rt)-val[u]);
if(fa[rt]) Insert(tree2[rt],dis(u,fa[rt])-val[u]);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v!=f&&!vis[v]) dfs(v,u,rt);
}
}
void solve(int u,int f){
fa[u]=f,vis[u]=;
int totsz=size;
dfs(u,,u);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]){
rt=,size=sz[v]>sz[u]?totsz-sz[u]:sz[v];
findrt(v,),to[u].push_back(rt),solve(rt,u);
}
}
}
void recover(int x){
//遍历点分树,清空节点
++size,vis[x]=;
Erholung(tree1[x]),Erholung(tree2[x]);
tree1[x]=tree2[x]=null;
for(int i=,k=to[x].size();i<k;++i) recover(to[x][i]);
to[x].clear();
}
void rebuild(int x){
//点分树某一子树过大,重构
size=,recover(x),rt=,findrt(x,);
if(fa[x])
for(int i=,j=to[fa[x]].size();i<j;++i)
if(to[fa[x]][i]==x) to[fa[x]][i]=rt;
solve(rt,fa[x]);
}
ll ans=;
int insert(int x){
register int i,ds,res=;
//求出小于等于val[x]-dis(x,fa[i])的个数,只要在平衡树里找小于val[x]-dis(x,fa[i])+1的就可以了
for(i=x;fa[i];i=fa[i])
ds=val[x]-dis(x,fa[i])+,ans+=getrk(tree1[fa[i]],ds)-getrk(tree2[i],ds);
Insert(tree1[x],-val[x]);
//然后维护修改
for(i=x;fa[i];i=fa[i]){
int dist=dis(fa[i],x)-val[x];
Insert(tree1[fa[i]],dist);
Insert(tree2[i],dist);
}
//考虑是否需要拍扁重建
for(i=x;fa[i];i=fa[i])
if(tree1[i]->sz>=tree1[fa[i]]->sz*alpha+) res=fa[i];
return res;
}
int main(){
n=read(),n=read();
register int i,j,b,x;
for(bin[]=i=;i<=;++i) bin[i]=bin[i-]<<;
while(bin[tp+]<=n) ++tp;
son[]=n+,rt=,init();
for(int i=;i<=n;++i){
fa[i]=f[i][]=read()^(ans%inf),b=read(),val[i]=read();
dep[i]=dep[f[i][]]+,len[i]=len[f[i][]]+b,vis[i]=;
if(fa[i]) to[fa[i]].push_back(i),add(f[i][],i);
for(j=;bin[j]+<=dep[i];++j) f[i][j]=f[f[i][j-]][j-];
x=insert(i);if(x) rebuild(x);
print(ans);
}
Ot();
return ;
}
bzoj3435 [Wc2014]紫荆花之恋(动态点分治+替罪羊树)的更多相关文章
- BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- luoguP3920 [WC2014]紫荆花之恋 动态点分治 + 替罪羊树
意外的好写..... 考虑点分 \(dis(i, j) \leq r_i + r_j\) 对于过分治中心一点\(u\),有 \(dis(i, u) - r_i = dis(j, u) + r_j\) ...
- [WC2014]紫荆花之恋(动态点分治+替罪羊思想)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- UOJ #55 & 洛谷 P3920 紫荆花之恋 —— 动态点分治+替罪羊树
题目:http://uoj.ac/problem/55 https://www.luogu.org/problemnew/show/P3920 参考博客:https://www.cnblogs.com ...
- uoj 55 紫荆花之恋 动态点分治+替罪羊式重构+treap
每插入一个点,直接把它当做重心插入原树,当做是动态点分树一样维护 但这样深度会越来越大,所以我们用类似替罪羊的方法 当树失去平衡时,对子树进行一次点分,保证复杂度 #include <cstdi ...
- 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT
[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...
- 【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)
Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...
- bzoj3435 [Wc2014]紫荆花之恋
如果这棵树不变的话,就是一个裸的点分树套平衡树,式子也很好推$di+dj<=ri+rj$,$ri-di>=dj-rj$ 平衡树维护$dj-rj$,然后查$ri-di$的$rank$即可. ...
随机推荐
- ADT离线安装
1.安装eclipse软件.安装后点击HELP菜单,找到下面的Install New Software并点击. 2.之后会弹出一个对话框,然后我们点击add,接下来弹出ADD对话框,然后我们再点击ar ...
- lib
E:\\Qt\\Qt5.12.2\\5.12.2\\msvc2017_64\\lib\\ ------------------------------------------------------- ...
- Ubuntu 17 Nginx 配置 laravel 运行环境
1 安装 nginx #aptitude install nginx #apatitude install php7.1-fpm 2 在 /etc/nginx/sites-available 建立 s ...
- Problem of Uninstall Cloudera: Cannot Add Hdfs and Reported Cannot Find CDH's bigtop-detect-javahome
1. Problem We wrote a shell script to uninstall Cloudera Manager(CM) that run in a cluster with 3 li ...
- 475. Heaters
static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...
- appium镜像设置
npm --registry http://registry.cnpmjs.org install -g appium 使用npm的国内镜像可以安装,速度很不错. 以后不想输入ip的话可以输入以下命令 ...
- RocketMQ 运维指令
1.1. 控制台使用 RocketMQ 提供有控制台及一系列控制台命令,用于管理员对主题,集群,broker 等信息的管理 登录控制台 首先进入RocketMQ 工程,进入/RocketMQ/bin ...
- tera term通过ttl脚本 自动连接服务器
在现在的这个公司一直使用tera term来远程连接服务器,感觉很方便,特别是它的ttl脚本配置的自动连接.有时候我们可能无法直接连接到目标服务器,需要通过ssh经过多个中间服务器才能连接到目标服务器 ...
- 项目中使用WCF替换asmx Web service总结
以前项目解决方案中,用http协议的asmx Web service作服务器数据访问入口,在SoapHeader中写入用户名和加盐密码进行身份认证. http asmx服务是明文传输,传输过程中数据很 ...
- AppleScript: Handler
AppleScript绝对是个奇葩的存在!不管功能有多强大. Handler有两种,一种是和OC类似的使用Label参数,一种是和javascript类似的使用括号把一堆参数都放在里面的. label ...