5問目まではサクサク解けたので、参加しておけばよかったな。
https://yukicoder.me/problems/no/899
問題
木を成す無向グラフが与えられる。
各頂点には初期状態で何匹かの妖精がおり、その数が入力として与えられる。
以下のクエリに順次答えよ。
- 頂点番号vが指定される。そこから距離2以下の頂点にいる妖精が、皆vに集まる。その際vにいる妖精数を答えよ。なお、移動した妖精の状態は、以降のクエリにも残る。
解法
木を根付き木とみなし、BFS順でEuler Tourしておこう。
加えて、各頂点に対し、そこから1回で移動できる頂点に関する情報(親頂点と、子頂点)と2回で移動できる頂点の情報(兄弟の頂点と孫頂点と親の親頂点)を考える。
BFS順でEulerTourすると、それぞれ区間で表現できる。
setに妖精数が正の頂点に関し(位置,妖精数)のpairを載せると、区間内の妖精を高速に集めることができる。
int N,Q; vector<int> E[101010]; int D[101010],P[101010]; int CL[101010],CR[101010],GL[101010],GR[101010]; int DN[101010],id[101010]; set<pair<int,ll>> S[101010]; void dfs(int cur,int pre,int d) { D[cur]=d; P[cur]=pre; id[cur]=++DN[D[cur]]; CL[cur]=GL[cur]=1<<20; CR[cur]=GR[cur]=-1; FORR(e,E[cur]) if(e!=pre) { dfs(e,cur,d+1); CL[cur]=min(CL[cur],id[e]); CR[cur]=max(CR[cur],id[e]); GL[cur]=min(GL[cur],CL[e]); GR[cur]=max(GR[cur],CR[e]); } } ll poke(int e) { ll tot=0; auto it=S[D[e]].lower_bound(make_pair(id[e],0)); if(it!=S[D[e]].end() && it->first==id[e]) { tot=it->second; S[D[e]].erase(it); } return tot; } ll poke_range(int d,int L,int R) { ll tot=0; while(1) { auto it=S[d].lower_bound({L,0}); if(it==S[d].end() || it->first>R) break; tot+=it->second; S[d].erase(it); } return tot; } void solve() { int i,j,k,l,r,x,y; string s; cin>>N; FOR(i,N-1) { cin>>x>>y; E[x].push_back(y); E[y].push_back(x); } dfs(0,0,0); FOR(i,N) { cin>>x; S[D[i]].insert({id[i],x}); } cin>>Q; while(Q--) { cin>>x; ll tot=poke(x); if(D[x]>=2) { tot+=poke(P[P[x]]); } if(D[x]>=1) { tot+=poke(P[x]); tot+=poke_range(D[x],CL[P[x]],CR[P[x]]); } tot+=poke_range(D[x]+1,CL[x],CR[x]); tot+=poke_range(D[x]+2,GL[x],GR[x]); S[D[x]].insert({id[x],tot}); cout<<tot<<endl; } }
まとめ
なんとなくζって6番目のイメージわかないんだよな。