BZOJ 3924: [Zjoi2015]幻想乡战略游戏 动态点分治
细节挺多的,但是也确实加深了我对动态点分治的理解.
这段代码值得关注:
ll query(int u) { ll re=calc(u); for(int i=hd[u];i;i=nex[i]) { ll t=calc(to[i]); if(t<re) return query(hto[i]); } return re; }
点分树的结构是和原树不同的.
我们知道,最优决策点和根节点的连线上的点的答案一定是越来越优的.
而如果发现 $to[i]$ 所在子树中,$to[i]$ 更优,那么想在点分树中到达 $to[i]$,直接走到 $to[i]$所在重心即可.
开始的时候思维太僵化了,一直想的是只靠重心之间转移,但其实我们也要将点分树和原树结合起来.
#include <cstdio> #include <vector> #include <algorithm> #define N 100004 #define inf 1000000000000000 #define ll long long #define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout) using namespace std; int edges,n; int hd[N],to[N<<1],nex[N<<1],val[N<<1], hto[N<<1]; void add(int u,int v,int c) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; } namespace tree { ll dis[N]; int fa[N],top[N],size[N],son[N],dep[N]; void dfs1(int u,int ff) { fa[u]=ff,size[u]=1; for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff) { int v=to[i]; dep[v]=dep[u]+1,dis[v]=dis[u]+1ll*val[i]; dfs1(v,u); size[u]+=size[v]; if(size[v]>size[son[u]]) son[u]=v; } } void dfs2(int u,int tp) { top[u]=tp; if(son[u]) dfs2(son[u],tp); for(int i=hd[u];i;i=nex[i]) if(to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i]); } int LCA(int x,int y) { while(top[x]!=top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]; return dep[x]<dep[y]?x:y; } ll Dis(int x,int y) { return dis[x]+dis[y]-2ll*dis[LCA(x,y)]; } }; vector<int>e[N]; int root,sn; int Fa[N],mx[N],size[N],vis[N]; ll F[N],G[N],num[N],tmp; void getroot(int u,int ff) { size[u]=1,mx[u]=0; for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff&&!vis[to[i]]) getroot(to[i],u),size[u]+=size[to[i]],mx[u]=max(mx[u],size[to[i]]); mx[u]=max(mx[u],sn-size[u]); if(mx[u]<mx[root]) root=u; } void dfs(int u,int ff) { size[u]=1; for(int i=hd[u];i;i=nex[i]) if(to[i]!=ff&&!vis[to[i]]) dfs(to[i],u),size[u]+=size[to[i]]; } void prepare(int u) { vis[u]=1; for(int i=hd[u];i;i=nex[i]) if(!vis[to[i]]) { dfs(to[i],u),root=0,sn=size[to[i]],getroot(to[i],u),hto[i]=root,Fa[root]=u,prepare(root); } } void Update(int u,ll v) { int U=u; num[u]+=v; for(;Fa[u];u=Fa[u]) { F[Fa[u]]+=v*tree::Dis(U,Fa[u]); G[u]+=v*tree::Dis(U,Fa[u]); num[Fa[u]]+=v; } } ll calc(int u) { ll re=F[u]; int U=u; for(;Fa[u];u=Fa[u]) { re+=F[Fa[u]]-G[u]+1ll*(num[Fa[u]]-num[u])*tree::Dis(U,Fa[u]); } return re; } ll query(int u) { ll re=calc(u); for(int i=hd[u];i;i=nex[i]) { ll t=calc(to[i]); if(t<re) return query(hto[i]); } return re; } int main() { int i,j,Q; // setIO("input"); scanf("%d%d",&n,&Q); for(i=1;i<n;++i) { int a,b,c; scanf("%d%d%d",&a,&b,&c),add(a,b,c),add(b,a,c); } int cc=0; tree::dfs1(1,0); tree::dfs2(1,1); sn=n,mx[0]=n,root=0,getroot(1,0),cc=root,prepare(root); for(int cas=1;cas<=Q;++cas) { int u,e; scanf("%d%d",&u,&e), Update(u,1ll*e); printf("%lld\n",query(cc)); } return 0; }