[ZJOI2015]幻想乡战略游戏
题解
由于所有边的边权是正整数,所以可以发现任何时刻每个点的答案是从最优的点往周围递增的。如果有平台,那么一定是在最优点的连通块那儿。
我们先考虑如何快速求每个点的答案。把式子拆开来:
f
u
=
∑
v
=
1
n
(
d
v
∗
d
i
s
u
,
v
)
=
∑
v
=
1
n
(
d
v
∗
(
d
e
p
u
+
d
e
p
v
−
2
∗
d
e
p
l
c
a
(
u
,
v
)
)
)
=
d
e
p
u
∗
(
∑
v
=
1
n
d
v
)
+
∑
v
=
1
n
(
d
v
∗
d
e
p
v
)
−
∑
v
=
1
n
(
d
v
∗
2
∗
d
e
p
l
c
a
(
u
,
v
)
)
f_u=\sum_{v=1}^n(d_v*dis_{u,v})\\ =\sum_{v=1}^n(d_v*(dep_u+dep_v-2*dep_{lca(u,v)}))\\ =dep_u*(\sum_{v=1}^nd_v)+\sum_{v=1}^n(d_v*dep_v)-\sum_{v=1}^n(d_v*2*dep_{lca(u,v)})
fu=v=1∑n(dv∗disu,v)=v=1∑n(dv∗(depu+depv−2∗deplca(u,v)))=depu∗(v=1∑ndv)+v=1∑n(dv∗depv)−v=1∑n(dv∗2∗deplca(u,v))可以发现前两部分都是可以
O
(
1
)
O(1)
O(1) 求的,第三部分我们可以用经典思路,把每个点的点权设为它的父亲边的长度,然后用树链剖分,每次修改把
x
x
x 到根的一条链的值全部减去2*点权,然后查询时只用求出到根的链上的值的和。
查询答案的问题解决了,剩下就可以直接用树剖在树上找最优点了。我们从任意一个点开始,它所在的重链的答案一定是单峰的,并且非峰顶的部分无平台,所以我们可以每条重链用平衡树维护之前的答案,然后在平衡树上二分找到峰顶。接下来全树最优点如果不是这个峰顶的话,必定要从这个峰顶走出这条重链去找,我们可以直接枚举与它相邻的点,那么此时最多有一个点的答案更优,走那个方向递归下去即可。
查询答案可以用全局平衡二叉树做到 O ( log n ) O(\log n) O(logn)。这个做法毛估一下大概是 O ( n log 3 n ) O(n\log^3n) O(nlog3n) 的,但是却跑得出奇地快(直接进入 O ( n log n ) ∼ O ( n log 2 n ) O(n\log n)\sim O(n\log^2 n) O(nlogn)∼O(nlog2n) 梯队),大概是因为其中的两个 log \log log 都跑不满的缘故。
代码
#include<bits/stdc++.h>//JZM yyds!!
#define ll long long
#define uns unsigned
#define IF (it->first)
#define IS (it->second)
#define END putchar('\n')
using namespace std;
const int MAXN=100005;
const ll INF=1e18;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
return f?x:-x;
}
int ptf[50],lpt;
inline void print(ll x,char c='\n'){
if(x<0)putchar('-'),x=-x;
ptf[lpt=1]=x%10;
while(x>9)x/=10,ptf[++lpt]=x%10;
while(lpt)putchar(ptf[lpt--]^48);
if(c>0)putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
struct edge{
int v,to;ll w;edge(){}
edge(int V,int T,ll W){v=V,to=T,w=W;}
}e[MAXN<<1];
int EN,G[MAXN];
inline void addedge(int u,int v,ll w){
e[++EN]=edge(v,G[u],w),G[u]=EN;
e[++EN]=edge(u,G[v],w),G[v]=EN;
}
int n,m,siz[MAXN],fa[MAXN],hs[MAXN];
ll dep[MAXN],d[MAXN],sd,sdv;
int hd[MAXN],id[MAXN],IN,tl[MAXN],tp[MAXN],rt[MAXN];
struct itn{
int ls,rs;
ll a,s,g,f,lz;itn(){}
itn(ll A){a=s=A,g=f=lz=0,ls=rs=0;}
}t[MAXN];
inline void cover(int x,ll z){
if(!x)return;
t[x].f+=t[x].s*z,t[x].g+=t[x].a*z,t[x].lz+=z;
}
inline void pushd(int x){
if(t[x].lz)
cover(t[x].ls,t[x].lz),cover(t[x].rs,t[x].lz),t[x].lz=0;
}
inline void update(int x){
t[x].s=t[x].a+t[t[x].ls].s+t[t[x].rs].s;
t[x].f=t[x].g+t[t[x].ls].f+t[t[x].rs].f;
}
inline void add(int x,int l,int r,int a,int b,ll d){
if(!x||l>r)return;
if(l>=a&&r<=b){cover(x,d);return;}
pushd(x);
if(x>=a&&x<=b)t[x].g+=t[x].a*d;
if(a<x)add(t[x].ls,l,x-1,a,b,d);
if(b>x)add(t[x].rs,x+1,r,a,b,d);
update(x);
}
inline ll query(int x,int l,int r,int a,int b){
if(!x||l>r)return 0;
if(l>=a&&r<=b)return t[x].f;
ll res=(x>=a&&x<=b?t[x].g:0);pushd(x);
if(a<x)res+=query(t[x].ls,l,x-1,a,b);
if(b>x)res+=query(t[x].rs,x+1,r,a,b);
return res;
}
inline int build(int l,int r){
if(l>r)return 0;
int p=0,s=0,x=l;
for(int i=l;i<=r;i++)s+=siz[id[i]]-siz[hs[id[i]]];
for(;x<r;x++){
p+=siz[id[x]]-siz[hs[id[x]]];
if((p<<1)>s)break;
}t[x]=itn(dep[id[x]]-dep[fa[id[x]]]);
t[x].ls=build(l,x-1),t[x].rs=build(x+1,r);
return update(x),x;
}
inline void dfs1(int x){
siz[x]=1,hs[x]=0;
for(int i=G[x];i;i=e[i].to){
int v=e[i].v;
if(v==fa[x])continue;
fa[v]=x,dep[v]=dep[x]+e[i].w;
dfs1(v),siz[x]+=siz[v];
if(siz[v]>siz[hs[x]])hs[x]=v;
}
}
inline void dfs2(int x){
hd[x]=++IN,id[IN]=x,tp[x]=(x==hs[fa[x]]?tp[fa[x]]:x),tl[tp[x]]=IN;
if(hs[x])dfs2(hs[x]);
for(int i=G[x];i;i=e[i].to){
int v=e[i].v;
if(v==fa[x]||v==hs[x])continue;
dfs2(v);
}if(x==tp[x])rt[x]=build(hd[x],tl[x]);
}
inline void addl(int x,ll z){
while(x)add(rt[tp[x]],hd[tp[x]],tl[tp[x]],hd[tp[x]],hd[x],z),x=fa[tp[x]];
}
inline ll schl(int x){
ll res=0;
while(x)res+=query(rt[tp[x]],hd[tp[x]],tl[tp[x]],hd[tp[x]],hd[x]),x=fa[tp[x]];
return res;
}
inline ll getv(int x){return dep[x]*sd+sdv+schl(x);}
#define pli pair<ll,int>
#define fi first
#define se second
inline pli schmn(int x,int l,int r,ll pr){
if(!x||l>r)return pli(INF,0);
ll pv=INF;pushd(x);
pli res=pli(pr+t[t[x].ls].f+t[x].g+dep[id[x]]*sd+sdv,id[x]);
if(fa[id[x]])pv=pr+t[t[x].ls].f+dep[fa[id[x]]]*sd+sdv;
if(pv<=res.fi)res=min(res,schmn(t[x].ls,l,x-1,pr));
else res=min(res,schmn(t[x].rs,x+1,r,pr+t[t[x].ls].f+t[x].g));
return res;
}
inline pli findp(int x){
pli as=schmn(rt[tp[x]],hd[tp[x]],tl[tp[x]],schl(fa[tp[x]]));
x=as.se;
for(int i=G[x];i;i=e[i].to){
int v=e[i].v;
if(tp[v]==tp[x])continue;
ll val=getv(v);
if(val<as.fi)as=pli(val,v);
}
if(as.se^x)return findp(as.se);
else return as;
}
signed main()
{
n=read(),m=read();
for(int i=1,u,v;i<n;i++)
u=read(),v=read(),addedge(u,v,read());
dfs1(1),dfs2(1);
for(int i=1;i<=m;i++){
int x=read(),e=read();
sd+=e,sdv+=e*dep[x],addl(x,-(e<<1));
print(findp(1).fi);
}
return 0;
}