点分树。
本题的核心问题在于不好直接确定补给站的位置。
但是仔细思考后我们发现,对于当前节点,如果存在一个儿子的答案比它优,那么补给站一定在那个儿子的子树中。
增量为 \(w\times(sum_u-2\cdot sum_v)\)。当 \(v\) 优于 \(u\) 时,\(2\cdot sum_v>sum_u\)。如果 \(v_2\) 也满足,则 \(sum_v+sum_{v2}>sum_u\),不符合条件,所以一定唯一。
又发现这棵树上的点的度数都不超过 \(20\),所以可以暴力递归,暴力跳,暴力算。复杂度 \(O(20n\log^2n)\)。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,q,tot,rt,siz[N],f[N],fa[N],pre[N],vis[N];
struct node{int v,w;node(int v=0,int w=0):v(v),w(w){}};
vector<node>adj[N];
vector<int>vec[N];
unordered_map<int,ll>dis[N];
void getg(int u,int lst){
siz[u]=1;f[u]=0;
for(int i=0;i<adj[u].size();++i){
int v=adj[u][i].v;if(v==lst||vis[v])continue;
getg(v,u);siz[u]+=siz[v];f[u]=max(f[u],siz[v]);
}
f[u]=max(f[u],tot-siz[u]);
if(f[u]<f[rt])rt=u;
}
void cal(int u,int lst,int tt){
for(int i=0;i<adj[u].size();++i){
int v=adj[u][i].v,w=adj[u][i].w;if(v==lst||vis[v])continue;
dis[tt][v]=dis[tt][u]+w;cal(v,u,tt);
}
}
int build(int u,int s){
rt=0;tot=s;dis[u][u]=0;getg(u,0);
int g=rt;vis[g]=1;dis[g][g]=0;cal(g,0,g);
for(int i=0;i<adj[g].size();++i){
int v=adj[g][i].v;if(vis[v])continue;
int tmp=build(v,s-f[v]);
vec[g].push_back(tmp);fa[tmp]=g;pre[tmp]=v;
}
return g;
}
ll d[N],s1[N],s2[N];
void update(int u,int c){
for(int i=u;i;i=fa[i]){
d[i]+=c;
s1[i]+=c*dis[i][u];
if(fa[i])s2[i]+=c*dis[fa[i]][u];
}
}
ll get(int u){
ll res=s1[u];
for(int i=u;fa[i];i=fa[i])res+=s1[fa[i]]-s2[i]+dis[fa[i]][u]*(d[fa[i]]-d[i]);
return res;
}
ll query(int u){
ll res=get(u);
for(int i=0;i<vec[u].size();++i){
int v=vec[u][i];
if(get(pre[v])<res)return query(v);
}
return res;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>q;
for(int i=1;i<n;++i){
int u,v,w;cin>>u>>v>>w;
adj[u].push_back(node(v,w));adj[v].push_back(node(u,w));
}
f[0]=1e9;
rt=build(1,n);
while(q--){
int u,c;cin>>u>>c;
update(u,c);
cout<<query(rt)<<endl;
}
return 0;
}
标签:node,暴力,int,题解,sum,adj,P3345
From: https://www.cnblogs.com/HQJ2007/p/17561383.html