BZOJ 3924 ZJOI2015 幻想乡战略游戏 树链剖分
题目链接:https://www.luogu.org/problemnew/show/P3345(bzoj权限题)
题意概述:动态维护树的上所有点到这棵树的带权重心的距离和。N,Q<=100000.
分析:
首先考虑一个性质,对于任意一个点i来说,如果存在一个儿子j使得sz[j]*2>tot,那么i一定不是树的重心,并且重心应该在j的子树中,否则重心就可以是i。(这里必须是事先积累的知识,证明简单,略)
于是就可以考虑一个暴力辣!每次从根出发按照上面的条件进行查找,一旦发现一个点没有儿子满足上面的性质,那么这个点就应该是一个重心。
但是这样一次次走很慢。注意到我们找的是深度最大的j满足sz[j]*2>tot(满足条件的j一定在一条链上面),这里有很多的做法,但是考虑到我们计算答案的时候可能用到树剖,所以我们选择在DFS序上面操作,维护区间的sz最大值,直接在线段树上来二分查找,一次查找当前重心的复杂度是O(logn)。
关于答案的计算,我们发现对于点i为重心的时候,其答案分成两部分,一个是i的子树,一个是i的祖先。我们单独计算每个祖先不包含i的其余子树的贡献,全部加起来就是这次的答案。
通过计算,一次的答案可以表示为:
sum[1]+d[i]*(tot-sz[i])-sum{ f(j) | j为i到1路径上的节(包括i)) }
f(i)=sz[i]*(d[i]-d[fa[i]])+(sz[fa[i]]-sz[i])*d[fa[i]]
sum[i]表示i的子树中的点到i的带权距离和,sz[i]表示i的子树中所有点的权值和,d[i]表示i到1的路径长度,tot表示所有点的权值和。
维护:树剖,维护所有的sz和f以及sz的最大值,重链顶端的点不要去维护,每一次跳到的时候单独计算其f(因为没有办法同时维护一个点所有的儿子的f)。每次修改一个点的权值之后,其到1路径上所有点的sz都会改变,我们发现这些点的f值会发生变化,同时在更新中我们到达的所有的点的重儿子的f也会发生变化,记得更新一下。
时间复杂度O(N+Q*logN^2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
typedef long long LL; int N,Q;
struct segment_tree{
static const int maxn=;
struct edge{ int to,next,w; }E[maxn<<];
int first[maxn],np,n,dist[maxn],_sz[maxn],fa[maxn],son[maxn],top[maxn];
int dfs_clock,l[maxn],a[maxn],sz[maxn];
int rt,np2,lc[maxn<<],rc[maxn<<],mx[maxn<<],flag[maxn<<],tot;
LL g[maxn<<],sum;
segment_tree(){ sum=np=np2=rt=; }
void add_edge(int u,int v,int w){
E[++np]=(edge){v,first[u],w};
first[u]=np;
}
void DFS1(int i,int f,int l){
fa[i]=f,dist[i]=l;
_sz[i]=,sz[i]=;
for(int p=first[i];p;p=E[p].next){
int j=E[p].to;
if(j==f) continue;
DFS1(j,i,l+E[p].w);
_sz[i]+=_sz[j];
if(_sz[j]>_sz[son[i]]) son[i]=j;
}
}
void DFS2(int i,int f,int tp){
top[i]=tp,l[i]=++dfs_clock;
a[dfs_clock]=i;
if(son[i]) DFS2(son[i],i,tp);
for(int p=first[i];p;p=E[p].next){
int j=E[p].to;
if(j==f||j==son[i]) continue;
DFS2(j,i,j);
}
}
void pushup(int now){
mx[now]=max(mx[lc[now]],mx[rc[now]]);
g[now]=g[lc[now]]+g[rc[now]];
}
void pushdown(int now,int L,int R){
if(!flag[now]) return;
int m=L+R>>;
if(top[a[L]]==a[L]) g[lc[now]]+=1ll*flag[now]*(dist[a[m]]-dist[a[L]]);
else g[lc[now]]+=1ll*flag[now]*(dist[a[m]]-dist[fa[a[L]]]);
if(top[a[m+]]==a[m+]) g[rc[now]]+=1ll*flag[now]*(dist[a[R]]-dist[a[m+]]);
else g[rc[now]]+=1ll*flag[now]*(dist[a[R]]-dist[a[m]]);
mx[lc[now]]+=flag[now],mx[rc[now]]+=flag[now];
flag[lc[now]]+=flag[now],flag[rc[now]]+=flag[now];
flag[now]=;
}
void build(int &now,int L,int R){
now=++np2,lc[now]=rc[now]=;
g[now]=mx[now]=flag[now]=;
if(L==R) return;
int m=L+R>>;
build(lc[now],L,m); build(rc[now],m+,R);
}
int query_p(int now,int L,int R){
if(L==R) return a[L];
pushdown(now,L,R);
int m=L+R>>;
if(mx[rc[now]]*>tot) return query_p(rc[now],m+,R);
return query_p(lc[now],L,m);
}
void query(int now,int L,int R,int pos){
if(L==R){
sz[a[L]]+=flag[now],flag[now]=;
return;
}
pushdown(now,L,R);
int m=L+R>>;
if(pos<=m) query(lc[now],L,m,pos);
else query(rc[now],m+,R,pos);
}
LL query_g(int now,int L,int R,int A,int B){
if(A<=L&&R<=B) return g[now];
pushdown(now,L,R);
int m=L+R>>;
if(B<=m) return query_g(lc[now],L,m,A,B);
if(A>m) return query_g(rc[now],m+,R,A,B);
return query_g(lc[now],L,m,A,B)+query_g(rc[now],m+,R,A,B);
}
void update1(int now,int L,int R,int A,int B,int delt){
if(A<=L&&R<=B){
mx[now]+=delt,flag[now]+=delt;
if(top[a[L]]==a[L]) g[now]+=1ll*delt*(dist[a[R]]-dist[a[L]]);
else g[now]+=1ll*delt*(dist[a[R]]-dist[fa[a[L]]]);
return;
}
pushdown(now,L,R);
int m=L+R>>;
if(B<=m) update1(lc[now],L,m,A,B,delt);
else if(A>m) update1(rc[now],m+,R,A,B,delt);
else update1(lc[now],L,m,A,B,delt),update1(rc[now],m+,R,A,B,delt);
pushup(now);
}
void update2(int now,int L,int R,int pos,int delt){
if(L==R){
sz[a[L]]+=flag[now],flag[now]=;
if(top[a[L]]!=a[L]) g[now]+=1ll*delt*dist[fa[a[L]]];
return;
}
pushdown(now,L,R);
int m=L+R>>;
if(pos<=m) update2(lc[now],L,m,pos,delt);
else update2(rc[now],m+,R,pos,delt);
pushup(now);
}
void update(int p,int delt){
tot+=delt,sum+=1ll*delt*dist[p];
while(p){
update1(rt,,n,l[top[p]],l[p],delt);
if(son[p]) update2(rt,,n,l[son[p]],delt);
p=fa[top[p]];
}
}
LL ans(){
int p=query_p(rt,,n);
query(rt,,n,l[p]);
LL re=sum+1ll*dist[p]*(tot-sz[p]);
while(p){
re-=query_g(rt,,n,l[top[p]],l[p]);
p=top[p];
query(rt,,n,l[p]); query(rt,,n,l[fa[p]]);
re-=1ll*sz[p]*(dist[p]-dist[fa[p]])+1ll*(sz[fa[p]]-sz[p])*dist[fa[p]];
p=fa[p];
}
return re;
}
}st; bool nega;
void _scanf(int &x)
{
x=,nega=;
char ch=getchar();
while((ch<''||ch>'')&&ch!='-') ch=getchar();
if(ch=='-') nega=,ch=getchar();
while(ch>=''&&ch<='') x=x*+ch-'',ch=getchar();
if(nega) x=-x;
}
int out_cnt,out[];
void _printf(LL x)
{
out[++out_cnt]=x%,x/=;
while(x) out[++out_cnt]=x%,x/=;
while(out_cnt) putchar(''+out[out_cnt--]);
putchar('\n');
}
void data_in()
{
_scanf(N);_scanf(Q); st.n=N;
int x,y,z;
for(int i=;i<N;i++){
_scanf(x);_scanf(y);_scanf(z);
st.add_edge(x,y,z); st.add_edge(y,x,z);
}
}
void work()
{
int u,e;
st.DFS1(,,);
st.DFS2(,,);
st.build(st.rt,,st.n);
for(int i=;i<=Q;i++){
_scanf(u);_scanf(e);
st.update(u,e);
_printf(st.ans());
}
}
int main()
{
data_in();
work();
return ;
}
BZOJ 3924 ZJOI2015 幻想乡战略游戏 树链剖分的更多相关文章
- bzoj 3924: [Zjoi2015]幻想乡战略游戏
Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来, ...
- bzoj 3924 [Zjoi2015]幻想乡战略游戏——动态点分治(暴力移动找重心)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3924 度数只有20,所以从一个点暴力枚举其出边,就能知道往哪个方向走. 知道方向之后直接走到 ...
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= = 不得不说CLJ说得真的太简单了,实现方面根本没提. 首先我们可以先用树分治构建出这 ...
- bzoj3924 [Zjoi2015]幻想乡战略游戏 点分树,动态点分
[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...
- 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治
[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...
- 洛谷 P3345 [ZJOI2015]幻想乡战略游戏 解题报告
P3345 [ZJOI2015]幻想乡战略游戏 题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做 ...
- [ZJOI2015]幻想乡战略游戏——动态点分治
[ZJOI2015]幻想乡战略游戏 带修改下,边点都带权的重心 随着变动的过程中,一些子树内的点经过会经过一些公共边.考虑能不能对这样的子树一起统计. 把树上贡献分块. 考虑点分治算法 不妨先把题目简 ...
- BZOJ3924 ZJOI2015 幻想乡战略游戏 【动态点分治】
BZOJ3924 ZJOI2015 幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂 ...
- AC日记——[ZJOI2015]幻想乡战略游戏 洛谷 P3345
[ZJOI2015]幻想乡战略游戏 思路: 树剖暴力转移: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1 ...
随机推荐
- 通过非聚集索引让select count(*) from 的查询速度提高几十倍、甚至千倍
通过非聚集索引,可以显著提升count(*)查询的性能. 有的人可能会说,这个count(*)能用上索引吗,这个count(*)应该是通过表扫描来一个一个的统计,索引有用吗? 不错,一般的查询,如果用 ...
- Folyd + 路径存储
一.Folyd 算法原理 如果 AB + AC < BC 那么, BC最短路就要经过 A. 在算法进行过程中,应该是 ,B-A 有很多路径,B 代表这些路径权值之和,A-C也有很多路径,C是这些 ...
- golang刷Leetcode系列 --- 实现strStr()
实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返 ...
- Yii2中使用Soap WebSerivce
Soap是一种轻量的.简单的.基于XML(标准通用标记语言下的一个子集)的协议 WebService顾名思义就是web服务,web服务主要有两种,一种是基于soap类型的服务,一种是基于rest类型的 ...
- linux命令sed与awk是干什么用的,怎么用?
非常强大的文本操纵工具,sed,awk,grep 这个三个命令 都是操作文本文件的unix系统有几个非常命令的特点:1. 对于内核而言,unix文件都是字节序列.io设备也是文件.2. 至于文件的含义 ...
- html+css3 实现各种loading效果
效果见下图 代码: 建议直接去本人github上浏览代码 https://github.com/wuliqiangqiang/loading <!DOCTYPE html> <htm ...
- 4 二维数组中的查找 JavaScript
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- ORA-12541:TNS:无监听程序问题
这种情况可能有多种原因,解决办法如下: 方法1.原因:监听日志listener.log过大,超过4. 步骤: a.暂停监听服务 b.删除listener.log,文件位置:E:\app\Adminis ...
- JS this总结
JS中一切皆对象,this关键字出现在对象定义时的成员(属性和方法)里,因此this指向的是一个JS对象,这个JS对象具体是哪一个的确定是在运行时确定的. 非严格模式: 1.作为对象成员:对象调用对象 ...
- lamp 安装 apache
lamp安装 httpd-2.2.4.tar.gz :http://download.csdn.net/detail/wulvla020311/8046141 先检查一下装的东西都在不在:rpm -q ...