题解 P3233 【[HNOI2014]世界树】

xukuan

2020-05-04 18:39:28

Solution

我来讲一下这题虚树的建法 1. 把所有会议室的点按根节点最小的$dfn$序排序,求出任意两点的$LCA$ 2. 把所有LCA的点和会议室的点及其相反数加进去,按$dfn$序(正的正序,负的倒序)排序之后$unique$ 3. 然后如果点的值$>0$进栈,$<0$连边然后出栈。 4. 然后$dp$,第$1$遍$dfs$求深度和两个$dfn$序,第$2$遍和第$3$遍求距离和编号的最小值,第$4$编统计答案 ~~代码贺了我们班大佬ckh的代码~~ ```cpp //自行加火车头 #include<bits/stdc++.h> #include<iostream> #include<cstdio> #define ll long long #define prll pair<ll,ll> #define mkpr make_pair using namespace std; const ll N=300010,INF=1ll<<30; ll n,m,cnt,top,a[N],b[N<<1],h[N],ans[N],d[N],size[N],p[N][30],v[N],st[N],dfnin[N],dfnout[N]; ll ver[N<<1],Next[N<<1],head[N],tot; ll _ver[N<<1],_Next[N<<1],_head[N],_tot; prll g[N]; inline ll read(){ ll x=0,tmp=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') tmp=-1; ch=getchar(); } while(isdigit(ch)){ x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return tmp*x; } inline void write(ll x){ if(x<0){ putchar('-'); x=-x; } ll y=10,len=1; while(y<=x){ y=(y<<3)+(y<<1); len++; } while(len--){ y/=10; putchar(x/y+48); x%=y; } } inline void addEdge(ll x,ll y){ ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } inline void _addEdge(ll x,ll y){ _ver[++_tot]=y; _Next[_tot]=_head[x]; _head[x]=_tot; } void dfs1(ll x,ll before){ d[x]=d[before]+1; size[x]=1; dfnin[x]=++cnt; p[x][0]=before; for(ll i=1; i<=20; i++) p[x][i]=p[p[x][i-1]][i-1]; for(ll i=head[x]; i; i=Next[i]){ ll y=ver[i]; if(y==before) continue; dfs1(y,x); size[x]+=size[y]; } dfnout[x]=++cnt; } void dfs2(ll x){ if(v[x]) g[x]=mkpr(0,x); else g[x]=mkpr(INF,0); for(ll i=_head[x]; i; i=_Next[i]){ ll y=_ver[i]; dfs2(y); g[x]=min(g[x],mkpr(g[y].first+d[y]-d[x],g[y].second)); } } void dfs3(ll x){ for(ll i=_head[x]; i; i=_Next[i]){ ll y=_ver[i]; g[y]=min(g[y],mkpr(g[x].first+d[y]-d[x],g[x].second)); dfs3(y); } } inline ll LCA(ll x,ll y){ if(d[x]>d[y]) swap(x,y); for(ll i=20; i>=0; i--){ if(d[x]<=d[p[y][i]]) y=p[y][i]; } if(x!=y){ for(ll i=20; i>=0; i--){ if(p[x][i]!=p[y][i]){ x=p[x][i]; y=p[y][i]; } } return p[x][0]; } return x; } inline ll getdis(ll x,ll y){ return d[x]+d[y]-2*d[LCA(x,y)]; } void dfs4(ll x){ ll u=g[x].second; for(ll i=_head[x]; i; i=_Next[i]){ ll y=_ver[i],v=g[y].second; if(u!=v){ ll dis=d[v]-(getdis(u,v)-(u<v))/2; for(ll j=20; j>=0; j--){ if(d[p[y][j]]>=dis) y=p[y][j]; } ans[u]-=size[y]; ans[v]+=size[y]; } dfs4(_ver[i]); } } void clean(ll x){ for(ll i=_head[x]; i; i=_Next[i]) clean(_ver[i]); ans[x]=_head[x]=v[x]=0; } inline bool cmp1(ll x,ll y){ return dfnin[x]<dfnin[y]; } inline bool cmp2(ll x,ll y){ return (x>0?dfnin[x]:dfnout[-x])<(y>0?dfnin[y]:dfnout[-y]); } int main(){ n=read(); for(ll i=1; i<n; i++){ ll x=read(),y=read(); addEdge(x,y); addEdge(y,x); } dfs1(1,0); for(ll T=read(); T; T--){ m=read(); for(ll i=1; i<=m; i++){ h[i]=a[i]=read(); v[a[i]]=1; } sort(a+1,a+1+m,cmp1); cnt=0; b[++cnt]=a[1]; b[++cnt]=-a[1]; for(ll i=2; i<=m; i++){ ll root=LCA(a[i-1],a[i]); b[++cnt]=root; b[++cnt]=-root; b[++cnt]=a[i]; b[++cnt]=-a[i]; } sort(b+1,b+1+cnt,cmp2); cnt=unique(b+1,b+1+cnt)-b-1; for(ll i=1; i<=cnt; i++){ if(b[i]>0) st[++top]=b[i]; else{ _addEdge(st[top-1],st[top]); st[top--]=0; } } dfs2(b[1]); dfs3(b[1]); ans[g[b[1]].second]=size[1]; dfs4(b[1]); for(ll i=1; i<=m; i++){ write(ans[h[i]]); putchar(' '); } putchar('\n'); clean(b[1]); } return 0; } ```