// 더 많은 정보와 풀이 해설은 42jerrykim.github.io에서 확인하세요.
#include<bits/stdc++.h>usingnamespacestd;intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);intn,m;if(!(cin>>n>>m))return0;vector<int>capacity(n+1);for(inti=1;i<=n;++i)cin>>capacity[i];vector<int>start(m+1);for(inti=1;i<=m;++i)cin>>start[i];constintMAX_IDX=2*n+5;constintINF=1e9;vector<int>remainingFood(MAX_IDX,0);vector<int>bestDistanceInSubtree(MAX_IDX,INF);vector<int>bestNodeInSubtree(MAX_IDX,0);vector<int>flowAlongEdge(MAX_IDX,0);autoleftChild=[](intu){returnu<<1;};autorightChild=[](intu){return(u<<1)|1;};autocostUp=[&](intu)->int{return(flowAlongEdge[u]<0)?-1:1;};autocostDown=[&](intchild)->int{return(flowAlongEdge[child]>0)?-1:1;};for(inti=1;i<=n;++i)remainingFood[i]=capacity[i];function<void(int)>updateNode=[&](intu){intbestPos=0;intbestDis=INF;if(u<=n&&remainingFood[u]>0){bestPos=u;bestDis=0;}intlc=leftChild(u);intcand=bestDistanceInSubtree[lc];if(cand<INF){intv=cand+costDown(lc);if(v<bestDis){bestDis=v;bestPos=bestNodeInSubtree[lc];}}intrc=rightChild(u);cand=bestDistanceInSubtree[rc];if(cand<INF){intv=cand+costDown(rc);if(v<bestDis){bestDis=v;bestPos=bestNodeInSubtree[rc];}}bestDistanceInSubtree[u]=bestDis;bestNodeInSubtree[u]=bestPos;};for(intu=n;u>=1;--u)updateNode(u);longlongtotalCost=0;for(inti=1;i<=m;++i){intx=start[i];intz=0;intnow=0;intbest=INF;for(intu=x;u>=1;u>>=1){intval=now+bestDistanceInSubtree[u];if(val<best){best=val;z=u;}if(u>1)now+=costUp(u);}inty=bestNodeInSubtree[z];totalCost+=best;--remainingFood[y];for(intu=x;u!=z;u>>=1)++flowAlongEdge[u];for(intu=y;u!=z;u>>=1)--flowAlongEdge[u];for(intu=y;u!=z;u>>=1)updateNode(u);for(intu=x;u>=1;u>>=1)updateNode(u);if(i>1)cout<<' ';cout<<totalCost;}cout<<'\n';return0;}
마무리
핵심은 “최단 증강경로”를 트리 구조와 dis/pos DP로 O(log n)에 찾고, 잔여 비용을 ±1로 정확히 반영하는 데 있다.