Nowcodercontest5278G血压游戏

做法非常多。。。

比如对于同一层的点直接建立虚树,然后模拟dp即可

如果不想建虚树,可以直接维护合并,每次合并得到的一定是同层点按照dfs序排序之后相邻两点的之一

处理出所有这样的LCA,然后按照dep从大到小依次操作

用一个set维护,每次取出子树区间里的点合并上来即可。。。

不知道怎么写


const int N=2e5+10;

int n,rt;
int a[N];
vector <int> G[N];
vector <pair <int,int> > E[N]; 
int fa[N][20],L[N],R[N],dfn,id[N],dep[N];
void dfs(int u,int f) {
    dep[u]=dep[fa[u][0]=f]+1;
    id[L[u]=++dfn]=u;
    rep(i,1,19) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int v:G[u]) if(v!=f) dfs(v,u);
    R[u]=dfn;
}
int LCA(int x,int y) {
    if(dep[x]<dep[y]) swap(x,y);
    for(int del=dep[x]-dep[y],i=0;(1<<i)<=del;++i) if(del&(1<<i)) x=fa[x][i];
    if(x==y) return x;
    drep(i,19,0) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}


ll ans;
int line[N];

set <pair <int,ll> > st;

void Solve(int d) {
    sort(E[d].begin(),E[d].end()); //按照dfs排序之后取相邻的LCA
    int n=E[d].size();
    int cnt=0;
    rep(i,1,n-1) line[++cnt]=LCA(id[E[d][i-1].first],id[E[d][i].first]);
    line[++cnt]=rt;
    sort(line+1,line+cnt+1,[&](int x,int y){ return (dep[x]>dep[y])||(dep[x]==dep[y] && x<y); }),cnt=unique(line+1,line+cnt+1)-line-1;
    st.clear();
    rep(i,0,n-1) st.insert(E[d][i]);
    rep(i,1,cnt) {
        int l=L[line[i]],r=R[line[i]];
        ll s=0;
        for(auto it=st.lower_bound(make_pair(l,0));it!=st.end();) {// set暴力维护合并
            if(it->first>r) break;
            s+=max(1ll,it->second-(dep[id[it->first]]-dep[line[i]]));
            auto tmp=it; ++it;
            st.erase(tmp);
        }
        st.insert(make_pair(L[line[i]],s));
    }
    ll t=st.begin()->second;
    ans+=max(1ll,t-1);
}

int main(){
    n=rd(),rt=rd();
    rep(i,1,n) a[i]=rd();
    rep(i,2,n) {
        int u=rd(),v=rd();
        G[u].pb(v),G[v].pb(u);
    }
    dfs(rt,0);
    rep(i,1,n) if(a[i]) E[dep[i]].pb(make_pair(L[i],a[i]));
    rep(i,1,n) if(E[i].size()) Solve(i);
    printf("%lld\n",ans);
}