BZOJ传送门
洛谷传送门
要计算所有点到一个点的带权距离和。
先考虑如何计算:
每次找重心,构造出点分树,对于树中的每个节点维护三个信息:
s v [ i ] sv[i] sv[i]:以 i i i为根的子树的点权和
s d [ i ] sd[i] sd[i]:以 i i i为根的子树中每个点到 i i i的带权距离和
s f [ i ] sf[i] sf[i]:以 i i i为根的子树中每个点到 i i i(在点分树中)的父亲的带权距离和
查询点i,就可以从i往上走,统计答案。
修改 i i i点的点权,同样是从 i i i点往上走,维护祖先的三个值
维护的时候需要求点 i i i到点分树中的祖先在原树中的距离,可以用nlogn预处理RMQ,O(1)求LCA(然而事实证明树剖求LCA更快。。亲测BZOJ树剖快10s )
因为点分树的树高是logn的,所以可以优雅地暴力往上爬
再考虑怎么找最优点。
修改是 O ( l o g n ) O(logn) O(logn)的,查询 O ( l o g 2 n ) O(log^2n) O(log2n),总复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
打完3h过去了。。
Code:
#include
#include
#include
#define LL long long
#define maxn 100005
using namespace std;
inline void read(int &a){
char c;bool f=0;
while(!isdigit(c=getchar())) if(c=='-') f=1;
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
if(f) a=-a;
}
int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot,info[maxn],cnt;
inline void line(int x,int y,int z){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;w[tot]=z;}
struct Edge{int u,v,o,nxt;}G[maxn];//o:origin path u->o
inline void Line(int x,int y,int o){G[++cnt]=(Edge){x,y,o,info[x]};info[x]=cnt;}
struct Origin_Tree{
int dis[maxn],st[maxn<<2][20],pt,pos[maxn],lg[maxn<<2];
void dfs(int u,int pre){
st[++pt][0]=dis[u],pos[u]=pt;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=pre)
dis[v]=dis[u]+w[i],dfs(v,u),st[++pt][0]=dis[u];
}
inline int getdis(int x,int y){
if(pos[x]>pos[y]) swap(x,y);
int k=lg[pos[y]-pos[x]+1];
return dis[x]+dis[y]-2*min(st[pos[x]][k],st[pos[y]-(1<<k)+1][k]);
}
void input(int n){
for(int i=1,x,y,z;i<n;i++) read(x),read(y),read(z),line(x,y,z),line(y,x,z);
dfs(1,0);
lg[0]=-1;for(int i=1;i<(n<<2);i++) lg[i]=lg[i>>1]+1;
for(int j=1;(1<<j)<=pt;j++)
for(int i=1;i+(1<<j)-1<=pt;i++)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}T;
int n,m,Fa[maxn],siz[maxn],f[maxn];
bool vis[maxn];
int spt,rt;
LL sv[maxn],sd[maxn],sf[maxn];
void getroot(int u,int pre){
siz[u]=1;f[u]=0;
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=pre){
getroot(v,u),siz[u]+=siz[v];
f[u]=max(f[u],siz[v]);
}
f[u]=max(f[u],spt-siz[u]);
if(f[u]<f[rt]) rt=u;
}
void Build(int u,int tsz){
vis[u]=1;
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]){
spt=(siz[v]<siz[u]?siz[v]:tsz-siz[u]),rt=0;//tsz!!
getroot(v,0),Line(u,rt,v);
Fa[rt]=u,Build(rt,siz[v]);
}
}
inline void insert(int u,int val){
sv[u]+=val;
for(int i=u;Fa[i];i=Fa[i]){
int dist=T.getdis(u,Fa[i]);//u -> Fa[i]!!
sd[Fa[i]]+=1ll*val*dist;
sf[i]+=1ll*val*dist;
sv[Fa[i]]+=val;
}
}
inline LL calc(int u){
LL ret=sd[u];
for(int i=u;Fa[i];i=Fa[i]) ret+=sd[Fa[i]]-sf[i]+(sv[Fa[i]]-sv[i])*T.getdis(Fa[i],u);
return ret;
}
LL query(int u){
LL tmp=calc(u);
for(int i=info[u];i;i=G[i].nxt) if(calc(G[i].o)<tmp) return query(G[i].v);
return tmp;
}
int main()
{
read(n),read(m);
T.input(n);
spt=f[0]=n,rt=0;getroot(1,0);
int root=rt,x,y;
Build(root,n);
while(m--){
read(x),read(y),insert(x,y);
printf("%lld\n",query(root));
}
}
u p d : upd: upd: