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 ...
随机推荐
- 【CodeForces 129 B】Students and Shoelaces(拓扑排序)
Anna and Maria are in charge of the math club for junior students. When the club gathers together, t ...
- Linux 学习第三天
一.常用命令 1.diff A.diff -q 源文件 目标文件 (快速比较文件是否相同) 2.ifconfig.nmcli (查看配置信息) 命令输入注意: Windows 查看网卡配置信息输入命 ...
- linux中删除文件内空白行的几种方法。
linux中删除文件内空白行的几种方法 有时你可能需要在 Linux 中删除某个文件中的空行.如果是的,你可以使用下面方法中的其中一个.有很多方法可以做到,但我在这里只是列举一些简单的方法. 你可能已 ...
- python函数名应用
函数名的应用 函数名 的应用分类: 函数就是一个特殊的变量(可以看成一个变量来用) *函数名对应函数的内存地址 *函数名可以做为容器类数据的元素 *函数名可以作为函数的参数 *函数名可以作为函数的返回 ...
- angular2或angular4中使用ckplayer播放rtmp和m3u8视频直播流
1. 下载ckpalyer整个包并导入, 将ckplayer放到src/assets/下 2. 引入ckplayer.js angular2中,在angular-cli.json中找到script,添 ...
- 我的Tmux学习笔记
0. 修改指令前缀 // ~/.tmux.conf ubind C-b set -g prefix C-a 1. 新建会话 tmux tmux new -s session-name // 可以设置会 ...
- STM32(12)——CAN
简介: CAN是Controller Area Network,是 ISO 国际标准化的串行通信协议. CAN 控制器根据两根线上的电位差来判断总线电平.总线电平分为显性电平和隐性电平,二者必居其一 ...
- 2017Facebook面试题改编“一面砖墙 ”
题目:一面砖墙 这道题改编自网上Facebook去年的一道面试题,是hihoCoder的1494题(https://hihocoder.com/problemset/problem/1494) 这道题 ...
- MySQL入门第一天——概述、数据表与约束操作
一.概述 1.安装 初学MySQL,我们下载msi的安装版:http://dev.mysql.com/downloads/file.php?id=457403 安装的过程文字简述可以参考之前随笔:ht ...
- 广州Uber优步司机奖励政策(12月14日到12月20日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...