Skip to content

树 题解

闲扯

好巧不巧今天 B 组 C 题也叫树。

军训回来复健力。

传送门

题解

由于直径没有什么好的性质,我们考虑把直径拆成两段长度相等的半径处理。

又由于直径的中点可能在某条边上,我们在每条边上加一个点,比如原先 $(u,v)$ 间有一条边,我们在其中加一个点 $e$ 变成 $(u,e),(e,v)$ 两条边。这样直径的中点就一定在一个点上了。

但这样计算直径就要除以二(半径),为防止出错我们将每次边权 $+1$ 变成 $+2$,这样就不会有问题了。

考虑把所有边权都加在叶子结点到父亲的边上,容易发现这样肯定是不劣的。

我们可以先做一遍树形 DP,求出每个非叶结点到叶子结点的最远距离 $r_i$每个非叶节点到叶子结点的距离和 $s_i$,另外还要统计叶子结点的个数 $c$。需要注意,为方便 DP,这里默认根节点度数不为 $1$,如果是 $1$ 就换一个。

然后如何维护我们没看懂题解,@李至擎 给我们讲了他的做法。

考虑假如确定中心点后,一个 $K$ 要加到树上有几种情况。

  1. 能把半径补齐,且能使得所有叶子结点到该结点的距离小于等于半径,此时 $K \le \frac{r_i \times c - s_i}{2}$,直径长度为 $r_i$(注意相对于原图是乘以二的)。
  2. 根本不够把另外一条边补得和半径一样长,容易发现这种情况必然存在另一个中心点满足情况 $1$,直接不考虑。
  3. 能补齐的情况下还多,此时最优策略显然是把多的平均分给叶子结点,假如这种情况下直径等于 $r_i+2x$,那么 $\frac{r_i \times c - s_i}{2} < K \le\frac{r_i \times c - s_i}{2}+x\times c$

为方便叙述,我们设 $ O_i=\frac{r_i \times c - s_i}{2}$。

容易发现情况 $1$ 的答案和 $K$ 无关,且按 $O_i$ 排序后恰好是一个后缀,直接维护一个后缀最小值即可。

对于情况 $3$,我们化一下式子发现 $x=\left\lceil\frac{k-O_i}{c}\right\rceil$,则答案为 $2\left\lceil\frac{k-O_i}{c}\right\rceil+r_i$

考虑进行一些化简降低耦合度,首先容易想到将上取整转化为分类讨论,即:

$$ \begin{cases} 2\left\lfloor\frac{k}{c}\right\rfloor + 2 - 2\left\lfloor\frac{O_i}{c}\right\rfloor + r_i & k\bmod c>O_i \bmod c \\ 2\left\lfloor\frac{k}{c}\right\rfloor-2\left\lfloor\frac{O_i}{c}\right\rfloor+r_i & k\bmod c\le O_i \bmod c \end{cases} $$

容易想到先把 $K$ 和 $O_i$ 离线下来从小到大排序,每次碰到 $K$ 维护一个指针把小于等于它的 $O_i$ 对应的 $- 2\left\lfloor\frac{O_i}{c}\right\rfloor+r_i$ 的全部加进一棵 按 $O_i \bmod c$ 维护线段树里,然后查询前缀、后缀最小值还有情况一的后缀最小值分别代式子就完事了。

有一堆细节要处理,具体看代码。

code

code
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define forup(i,s,e) for(i64 i=(s);i<=(e);i++)
#define fordown(i,s,e) for(i64 i=(s);i>=(e);i--)
using namespace std;
using i64=long long;
#define gc getchar()
inline i64 read(){
    i64 x=0,f=1;char c;
    while(!isdigit(c=gc)) if(c=='-') f=-1;
    while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=gc;}
    return x*f;
}
#undef gc
const i64 N=4e5+5,inf=1e18;
i64 n,q,cnte,lf;
vector<i64> e[N];
struct Node{
    i64 mx,se;
    Node():mx(-inf),se(-inf){};
    void operator()(const i64 &r){
        if(r>=mx){
            se=mx;mx=r;
        }else if(r>se){
            se=r;
        }
    }
};
Node dp[N],pd[N];
i64 pd1[N],sum[N],sz[N],rt=1;
void dfs(i64 x,i64 fa){
    sum[x]=0;
    for(auto i:e[x]){
        if(i==fa) continue;
        dfs(i,x);
        sum[x]+=sum[i]+sz[i];
        sz[x]+=sz[i];
        dp[x](dp[i].mx+1);
    }
    if(e[x].size()==1){
        sz[x]=1;
        dp[x](0);
    }
}
void dfs1(i64 x,i64 fa){
    for(auto i:e[x]){
        if(i==fa) continue;
        pd[i](dp[i].mx);
        pd[i](dp[i].se);
        if(pd[x].mx==dp[i].mx+1){
            pd[i](pd[x].se+1);
        }else{
            pd[i](pd[x].mx+1);
        }
        pd1[i]=pd1[x]+sz[rt]-sz[i]*2;
        dfs1(i,x);
    }
}
i64 o[N];
struct SegmentTree{
    #define mid ((l+r)>>1)
    #define lson l,mid,id<<1
    #define rson mid+1,r,id<<1|1
    i64 querymin[N<<1];
    void clear(){
        mem(querymin,0x3f);
    }
    void PushUp(i64 id){
        querymin[id]=min(querymin[id<<1],querymin[id<<1|1]);
    }
    void Update(i64 X,i64 P,i64 l=0,i64 r=lf,i64 id=1){
        if(l==r){
            querymin[id]=min(querymin[id],P);
            return;
        }
        if(X<=mid) Update(X,P,lson);
        else       Update(X,P,rson);
        PushUp(id);
    }
    i64 Ask(i64 L,i64 R,i64 l=0,i64 r=lf,i64 id=1){
        if(L<=l&&r<=R){
            return querymin[id];
        }
        i64 res=inf;
        if(L<=mid) res=min(res,Ask(L,R,lson));
        if(mid< R) res=min(res,Ask(L,R,rson));
        return res;
    }
}mt;
struct node{
    i64 op,r;
}nds[N];
bool cmp(node &a,node &b){
    return a.op<b.op;
}
i64 suf[N];
struct ques{
    i64 pos,num;
}qq[N];
i64 ans[N];
bool cmp1(ques &a,ques &b){
    return a.num<b.num;
}
i64 cntn;
signed main(){
    cnte=n=read();
    mt.clear();
    forup(i,1,n-1){
        i64 u=read(),v=read();++cnte;
        e[u].push_back(cnte);e[cnte].push_back(u);
        e[v].push_back(cnte);e[cnte].push_back(v);
    }
    forup(i,1,cnte){
        if(e[i].size()==1){
            lf++;
        }
    }
    forup(i,1,cnte){
        if(e[i].size()>1){
            rt=i;
            break;
        }
    }
    dfs(rt,0);
    pd[rt]=dp[rt];
    pd1[rt]=sum[rt];
    dfs1(rt,0);
    forup(i,1,cnte){
        o[i]=(pd[i].mx*lf-pd1[i])/2;
        if(e[i].size()>1) nds[++cntn]=node{o[i],pd[i].mx};
    }
    sort(nds+1,nds+cntn+1,cmp); 
    suf[cntn+1]=inf;
    fordown(i,cntn,0){
        suf[i]=min(suf[i+1],nds[i].r);
    }
    q=read();
    forup(i,1,q){
        qq[i].num=read();
        qq[i].pos=i;
    }
    sort(qq+1,qq+n+1,cmp1);
    i64 l=1;
    forup(i,1,q){
        while(l<=cntn&&nds[l].op<=qq[i].num){
            mt.Update(nds[l].op%lf,-2*(nds[l].op/lf)+nds[l].r);
            l++;
        }
        i64 res=inf;
        res=min(res,suf[l]);
        res=min(res,2*(qq[i].num/lf)+2+mt.Ask(0,qq[i].num%lf));
        res=min(res,2*(qq[i].num/lf)+mt.Ask(qq[i].num%lf,lf-1));
        ans[qq[i].pos]=res;
    }
    forup(i,1,q){
        printf("%lld\n",ans[i]);
    }
}

Comments