传送门


调了两个小时,终于过了……

凭啥人家代码80行我180行啊!!!

谁叫你大括号换行

谁叫你写缺省源


思路

显然,补给点所在的位置就是这棵树的带权重心。

考虑size已知时如何找重心:一开始设答案在\(x\),若存在\(x\)的一个子节点\(v\),使\(size_v>sum-size_v\),即\(2size_v>sum\),就往\(v\)走,重复该过程,直到走不动为止。

考虑用点分树优化这一过程:由于点分树层数不超过\(\log n\),这题又保证\(deg\leq 20\),可以暴力往下跳。

然而由于在点分树上跳,与原树有一些区别,需要搞一些奇怪的东西。

假设点分树上\(x\)点有\(v\)点为儿子且满足上述要求,在原树上有\(x\rightarrow w\)这条边,且\(w\)在\(v\)的点分树的子树里,那么就要把除了\(v\)这个连通块以外的\(size\)加到\(w\)上。

即:\(size_w+=size_x-size_v\),其中\(size\)表示点分树上的子树的\(\sum d\)。(不明白为什么要叫作\(d\),叫\(val\)不好么?)

求出重心后还要把刚才的修改操作撤销。

得到重心之后就很好做了,沿点分树往上跳就好了。

需要维护以下几样东西:

点分树上有节点\(x\),它在点分树上的父亲是\(fa\),它在点分树上的子树为\(S\),则有:

\[\begin{align*}
&sumv_x=\sum_{u\in S} val_u\\
&sumV_x=\sum_{u\in S} val_u dis(u,x)\\
&sumFV_x=\sum_{u\in S} val_u dis(u,fa)
\end{align*}
\]

记录这几样东西就可以统计答案了。

听着还好,写的时候思路很容易乱,务必要理清。

不然您就会像我一样记录了一堆东西全都没用

假设\(q,n\)同阶,则最后复杂度\(O(20n\log n)\)?反正能过就行,而且还挺快。


代码

#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define sz 101010
typedef long long ll;
template<typename T>
inline void read(T& t)
{
t=0;char f=0,ch=getchar();
double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.')
{
ch=getchar();
while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();
}
t=(f?-t:t);
}
template<typename T,typename... Args>
inline void read(T& t,Args&... args){read(t); read(args...);}
void file()
{
#ifndef ONLINE_JUDGE
freopen("a.txt","r",stdin);
#endif
}
inline bool chkmin(int &x,int y){return x>y?x=y,1:0;}
inline bool chkmax(int &x,int y){return x<y?x=y,1:0;}
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std; #define v edge[i].t
namespace T
{
struct hh{int t;ll w;int nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t,ll w)
{
edge[++ecnt]=(hh){t,w,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,w,head[t]};
head[t]=ecnt;
}
ll val[sz];
ll Dep[sz];
void dfs(int x,int fa){ go(x) if (v!=fa) { Dep[v]=Dep[x]+edge[i].w; dfs(v,x); } }
bool vis[sz];
int size[sz],sum,rt,rtS;
void calcS(int x,int fa)
{
size[x]=1;
go(x) if (v!=fa&&!vis[v]) { calcS(v,x); size[x]+=size[v]; }
}
void findrt(int x,int fa)
{
int S=sum-size[x];
go(x) if (v!=fa&&!vis[v]) { findrt(v,x); chkmax(S,size[v]); }
if (chkmin(rtS,S)) rt=x;
}
int findrt(int x){ calcS(x,0); sum=size[x]; rtS=1e9; findrt(x,0); return rt; }
}
#define goT(x) for (int i=T::head[x];i;i=T::edge[i].nxt)
#define Tv T::edge[i].t
namespace S
{
int root;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t){edge[++ecnt]=(hh){t,head[f]};head[f]=ecnt;}
ll sumv[sz],sumV[sz],sumFV[sz];
int ff[sz];
struct hhh{int u,uu;ll dis;};
vector<hhh>in[sz];
void calcDis(int x,int fa,int u,int uu,ll dis)
{
in[x].push_back((hhh){u,uu,dis});
goT(x) if (Tv!=fa&&!T::vis[Tv]) calcDis(Tv,x,u,uu,dis+T::edge[i].w);
}
void build(int x)
{
T::vis[x]=1;
goT(x) if (!T::vis[Tv])
{
int rt=T::findrt(Tv);
make_edge(x,rt);
calcDis(Tv,0,x,rt,T::edge[i].w);
ff[rt]=Tv;
build(rt);
}
}
void build(){root=T::findrt(1);build(root);}
void change(int x,ll s) // val_x+=s
{
T::val[x]+=s;sumv[x]+=s;
drep(t,(int)in[x].size()-1,0)
{
int u=in[x][t].u,uu=in[x][t].uu;ll D=in[x][t].dis*s;
sumv[u]+=s;sumV[u]+=D;sumFV[uu]+=D;
}
}
ll getans(int x)
{
ll ret=sumV[x];
drep(t,(int)in[x].size()-1,0)
{
int u=in[x][t].u,uu=in[x][t].uu;ll dis=in[x][t].dis;
go(u) if (v!=uu) ret+=sumFV[v]+sumv[v]*dis;
ret+=T::val[u]*dis;
}
return ret;
}
int getroot(int x)
{
go(x) if (sumv[v]*2>sumv[x])
{
int delta=sumv[x]-sumv[v];
change(ff[v],delta);
int ret=getroot(v);
change(ff[v],-delta);
return ret;
}
return x;
}
ll getans(){return getans(getroot(root));}
} int n,Q; int main()
{
file();
read(n,Q);
int x,y,z;
rep(i,1,n-1) read(x,y,z),T::make_edge(x,y,z);
S::build();
while (Q--)
{
read(x,y);
S::change(x,y);
printf("%lld\n",S::getans());
}
}

(压了一下行,只剩\(151\)行了)

(反正我写这么丑也没人看QwQ)

洛谷P3345 [ZJOI2015]幻想乡战略游戏 [动态点分治]的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治

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

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

    题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...

  9. 【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治

    题目描述 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打 ...

随机推荐

  1. 关闭Android ActionBar

    修改Styles.xml中 <resources> <!-- Base application theme. --> <style name="AppTheme ...

  2. JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法

    $(function(){ pushHistory(); window.addEventListener("popstate", function(e) { alert(" ...

  3. 【bzoj 3173】[Tjoi2013]最长上升子序列

    Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一 ...

  4. 【BZOJ】2286: [Sdoi2011]消耗战 虚树+DP

    [题意]给定n个点的带边权树,每次询问给定ki个特殊点,求隔离点1和特殊点的最小代价.n<=250000,Σki<=500000. [算法]虚树+DP [题解]考虑普通树上的dp,设f[x ...

  5. Linux之Ubuntu添加/移除个人软件包存档的源[PPA,Personal Package Archives]

    现在很多第三方软件还需要添加PPA软件源到Ubuntu系统当中,但是添加了PPA之后,软件可以直接在软件中心进行安装并会自动提示升级,这就是Ubuntu带来的方便,现在我们就来看看如何添加PPA软件源 ...

  6. I - Magic FZU - 2280 (字符串hash)

    题目链接: I - Magic FZU - 2280 学习链接: FZU - 2280 I - Magic 题目大意: 给你nn个字符串,每个字符串有一个值ww,有qq次询问,一共两种操作:一是“1, ...

  7. 资源中心的ES 服务的COM.IFLYTEK.ERSP.API.RESOURCEAPI 接口注册ZOOKEEPER失败,解决记录

    No provider available for the service com.iflytek.ersp.api.ResourceApi from the url zookeeper://zook ...

  8. Django中的缓存基础知识

    由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5 ...

  9. Linux 创建交换分区扩展虚拟内存

    当计算机的物理内存不足时,可以利用磁盘空间扩张为物理内存,实现的方式则是创建交换分区. 命令:mkswap + 分区设备 (格式化交换分区)     mkswapon +分区设备 (启用交换分区)   ...

  10. P5270 无论怎样神树大人都会删库跑路

    题目地址:P5270 无论怎样神树大人都会删库跑路 第一眼看上去是模拟,似乎是 \(O(n)\) 的 水题 信心满满的写完: #include <bits/stdc++.h> using ...