传送门

考虑先随便找一个点作为根,然后再慢慢移动根,这样一步步走到最优的点

设 $sum[x]$ 表示节点 $x$ 的子树的军队数,$len(x,y)$ 表示 $x,y$ 之间边的长度

那么对于根节点 $x$ 的一个儿子 $v$,考虑把儿子搞为根时,代价的改变量

$v$ 的子树内的军队消耗减少,共减少了 $sum[v]\cdot len(x,v)$

$v$ 的子树外的军队消耗增加,即根节点 $x$ 的子树内除了 $v$ 子树的军队消耗增加

代价增加了 $(sum[x]-sum[v])\cdot len(x,y)$

如果儿子比父亲优,那么整理可得 $2sum[v]>sum[x]$,显然满足条件的 $v$ 只有一个

此时如果没有满足的 $v$ ,那么 $x$ 就是最优点,否则最优点在 $x$ 的子树内

如果每次都一个一个儿子跳下去,显然会GG

但是因为最优点在子树内所以可以考虑在点分树上跳

我们需要维护两个东西 : $S[x],Sf[x]$,分别表示节点 $x$的点分树子树到 $x$ 的总代价,节点 $x$ 的点分树子树到 $x$ 在点分树父亲 $Fa[x]$ 的总代价

那么计算一个节点 $x$ 的总消耗就考虑一直往 $Fa$ 跳,每次跳完就考虑这一段产生的代价

设当前跳到了节点 $now$

那么十分显然 $Fa[now]$ 的点分树子树 不包括 $now$ 的点分树子树 的部分新产生的代价为 $S[Fa[now]]-Sf[now]+(sum[Fa[now]]-sum[now])\cdot dis(Fa[now],x)$

($dis(x,y)$表示节点 $x,y$ 在原树上的距离,注意此时 $sum[x]$ 表示节点 $x$ 的点分树子树军队总数)

我们可以用 RMQ 求 LCA 来 $O(1)$ 求出两点间的距离

至于修改操作也在点分树上直接维护就好了

注意$long long$,代码有注释

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=2e5+,INF=1e9+;
int fir[N],from[N],to[N],val[N],cntt;
inline void add(int &a,int &b,int &c)
{
from[++cntt]=fir[a]; fir[a]=cntt;
to[cntt]=b; val[cntt]=c;
}
int n,m,tot,rt;
int sz[N],mx[N],Fa[N];
vector <int> V[N],G[N];//存点分树
bool vis[N];
void find_rt(int x,int fa)
{
sz[x]=; mx[x]=;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(v==fa||vis[v]) continue;
find_rt(v,x); sz[x]+=sz[v];
mx[x]=max(mx[x],sz[v]);
}
mx[x]=max(mx[x],tot-sz[x]);
if(mx[x]<mx[rt]) rt=x;
}
void build(int x)//建点分树
{
vis[x]=;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(vis[v]) continue;
tot=sz[v]; rt=; find_rt(v,);
V[x].push_back(rt); G[x].push_back(v);
Fa[rt]=x; build(rt);
}
}
int st[N],dfn[N],pos[N],dis[N],Top,dfs_clock,f[N][],Log[N];//维护RMQ求LCA维护dis
void dfs(int x,int fa)
{
dfn[x]=++dfs_clock; st[++Top]=x; pos[x]=Top;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(v==fa) continue;
dis[v]=dis[x]+val[i]; dfs(v,x);
st[++Top]=x;
}
}
void pre()
{
Log[]=-; for(int i=;i<=Top;i++) Log[i]=Log[i>>]+;
for(int i=;i<=Top;i++) f[i][]=st[i];
for(int i=;(<<i)<=Top;i++)
for(int j=;j+(<<i-)<=Top;j++)
{
if(dfn[f[j][i-]]<dfn[ f[j+(<<i-)][i-] ]) f[j][i]=f[j][i-];
else f[j][i]=f[j+(<<i-)][i-];
}
}
inline int LCA(int x,int y)
{
int l=pos[x],r=pos[y]; if(l>r) swap(l,r);
int k=Log[r-l+];
if(dfn[f[l][k]]<dfn[f[r-(<<k)+][k]]) return f[l][k];
return f[r-(<<k)+][k];
}
inline int Dis(int x,int y) { return dis[x]+dis[y]-*dis[LCA(x,y)]; }
ll sum[N],S[N],Sf[N];//注意long long
inline void change(int x,int y)//修改操作
{
sum[x]+=y;
for(int now=x;Fa[now];now=Fa[now])//在点分树上跳
{
int d=Dis(x,Fa[now]);
Sf[now]+=1ll*d*y;
S[Fa[now]]+=1ll*d*y;
sum[Fa[now]]+=y;
}
}
inline ll calc(int x)//计算以x为根的花费
{
ll res=S[x];
for(int now=x;Fa[now];now=Fa[now])
{
int d=Dis(x,Fa[now]);
res+=S[Fa[now]]-Sf[now]+(sum[Fa[now]]-sum[now])*d;
}
return res;
}
ll query(int x)//点分树上暴力dfs找最优解
{
ll res=calc(x); int len=V[x].size();
for(int i=;i<len;i++)
{
ll t=calc(G[x][i]);//注意是算G[x][i]
if(t<res) return query(V[x][i]);//注意是往V[x][i]跳
}
return res;
}
int main()
{
int a,b,c,RT;
n=read(); m=read();
for(int i=;i<n;i++)
{
a=read(),b=read(),c=read();
add(a,b,c); add(b,a,c);
}
tot=n; mx[]=INF;
find_rt(,); RT=rt; build(rt);
dfs(,); pre();
while(m--)
{
a=read(); b=read();
change(a,b);
printf("%lld\n",query(RT));
}
return ;
}

P3345 [ZJOI2015]幻想乡战略游戏的更多相关文章

  1. 洛谷 P3345 [ZJOI2015]幻想乡战略游戏 解题报告

    P3345 [ZJOI2015]幻想乡战略游戏 题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做 ...

  2. P3345 [ZJOI2015]幻想乡战略游戏 动态点分治

    \(\color{#0066ff}{ 题目描述 }\) 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越 ...

  3. 洛谷P3345 [ZJOI2015]幻想乡战略游戏 [动态点分治]

    传送门 调了两个小时,终于过了-- 凭啥人家代码80行我180行啊!!! 谁叫你大括号换行 谁叫你写缺省源 思路 显然,补给点所在的位置就是这棵树的带权重心. 考虑size已知时如何找重心:一开始设答 ...

  4. 洛谷P3345 [ZJOI2015]幻想乡战略游戏(动态点分治,树的重心,二分查找,Tarjan-LCA,树上差分)

    洛谷题目传送门 动态点分治小白,光是因为思路不清晰就耗费了不知道多少时间去gang这题,所以还是来理理思路吧. 一个树\(T\)里面\(\sum\limits_{v\in T} D_vdist(u,v ...

  5. 2018.08.28 洛谷P3345 [ZJOI2015]幻想乡战略游戏(点分树)

    传送门 题目就是要求维护带权重心. 因此破题的关键点自然就是带权重心的性质. 这时发现直接找带权重心是O(n)的,考虑优化方案. 发现点分树的树高是logn级别的,并且对于以u为根的树,带权重心要么就 ...

  6. BZOJ 3924 / Luogu P3345 [ZJOI2015]幻想乡战略游戏 (动态点分治/点分树)

    题意 树的结构不变,每个点有点权,每一条边有边权,有修改点权的操作,设xxx为树中一点.求∑idist(x,i)∗a[i]\sum_idist(x,i)*a[i]i∑​dist(x,i)∗a[i]的最 ...

  7. AC日记——[ZJOI2015]幻想乡战略游戏 洛谷 P3345

    [ZJOI2015]幻想乡战略游戏 思路: 树剖暴力转移: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1 ...

  8. [ZJOI2015]幻想乡战略游戏——动态点分治

    [ZJOI2015]幻想乡战略游戏 带修改下,边点都带权的重心 随着变动的过程中,一些子树内的点经过会经过一些公共边.考虑能不能对这样的子树一起统计. 把树上贡献分块. 考虑点分治算法 不妨先把题目简 ...

  9. BZOJ3924 ZJOI2015 幻想乡战略游戏 【动态点分治】

    BZOJ3924 ZJOI2015 幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂 ...

随机推荐

  1. if else的执行流程

    int main(void) { int a, b; char op; float ans; scanf_s("%d%c%d",&a,&op,1,&b); ...

  2. while循环 for循环的理解

    不管是while循环还是for循环都隐含着一个if else的结构,就是说,if 条件满足,那么就执行循环体内部的语句,else就做循环体外部的事情. 有一个例子我觉得特别典型,程序内部定义了一个特定 ...

  3. ubuntu下安装配置apache2(含虚拟主机配置)

    在Ubuntu14.14中安装apache 安装指令: sudo apt-get install apache2 安装结束后: 产生的启动和停止文件是: /etc/init.d/apache2 启动: ...

  4. js日期 操作

    //重写toString方法,将时间转换为Y-m-d H:i:s格式 Date.prototype.toString = function(){ ) + "-" + this.ge ...

  5. Java web 三层架构 模拟图

  6. HttpClient connectionTimeout

    转自:http://www.cnblogs.com/carlosk/archive/2013/03/12/2956502.html 前几天服务器端的产品经理跑来问我是否有做请求超时和响应超时的处理.我 ...

  7. Alpha冲刺(三)

    Information: 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Details: 组员1(组长)柯奇豪 过去两天完成了哪些任务 ssm框架的使用并实现简单的数据处理 ...

  8. MongoDB整理笔记のCRUD

    添加 下面我们来建立一个test 的集合并写入一些数据.建立两个对象j 和t , 并保存到集合中去.在例子里 “>” 来表示是 shell 输入提示符    > j = { name : ...

  9. is/as操作符

    is/as操作符,是C#中用于类型转换的,提供了对类型兼容性的判断,从而使得类型转换控制在安全的范畴,提供了灵活的类型转换控制. is规则:检查对象的兼容性,查看对象的类型是否完全一样,返回值 tru ...

  10. duilib入门简明教程 -- 前言(1)

        关于duilib的介绍就不多讲了,一来不熟,二来小伙伴们想必已经对比了多个界面库,也无需赘述.下面进入正题:     不看广告看疗效! 已有众多知名公司采用duilib做为界面库,如华为网盘. ...