简单题选做 「[CSP2019]树的重心」
说一种比较无脑的主席树思路。
首先考虑部分分做法?
然后考虑满分怎么做。以下设
这个满分做法,我自己推的比较烦,不如其他人简洁。大概就是计算每个点当重心的次数。然后我分成了三部分算。对于一个点
Case 1 它子树内的某个子树被割了,它成为了重心
考虑此时这个小子树的子树大小需要满足什么条件。首先由于要分子树讨论,不妨设从
其中
于是这部分就可以分子树查。发现本质上转化成了在
Case 2 它子树外的某个子树被割了,它成为了重心
此时还是不变,但是约束变为了
这部分也是可以直接主席树来求。但是注意解出来的
Case 3 它子树外某个不是子树的连通块被割了,它成为了重心
考虑此时割掉的一定是
于是还是维护一棵退栈顺序的线段树来维护这个。
然后以上的线段树可以换成树状数组。于是代码:
int _bit[N] ;
#define low(x) (x & -x)
il void mdf(int p, int x){
for ( ; p <= n ; p += low(p)) _bit[p] += x ;
}
il int ask(int p){
int ret = 0 ;
for ( ; p ; p -= low(p)) ret += _bit[p] ;
return ret ;
}
il int qry(int l, int r){ return ask(r) - ask(l - 1) ; }
void dfs5(int x, int fa){
int l = max(1, n - 2 * sz[x]), s ;
int r = n - 2 * sz[mson[x]], k, y, g ;
if (r >= l){//Case 2
res[x] += query(_rt[0], _rt[dfn[x] - 1], 1, n, l, r) ;
res[x] += query(_rt[dfn[x] + sz[x] - 1], _rt[n], 1, n, l, r) ;
res[x] -= qry(l, r) ;
}
pre[0] = 0 ; s = edg[x].size() ; suf[s] = 0 ;
for (k = 0 ; k < s ; ++ k){
y = edg[x][k] ;
if (y == fa){ pre[k] = pre[max(0, k - 1)] ; continue ; }
pre[k] = max(pre[max(k - 1, 0)], sz[y]) ;
}
for (k = s - 1 ; ~k ; -- k){
y = edg[x][k] ;
if (y == fa){ suf[k] = suf[k + 1] ; continue ; }
suf[k] = max(suf[k + 1], sz[y]) ;
}
for (k = 0 ; k < s ; ++ k){ //Case 1
y = edg[x][k] ;
if (y == fa) continue ;
g = max(n - sz[x], suf[k + 1]) ;
if (k > 0) g = max(pre[k - 1], g) ;
l = max(1, 2 * sz[y] - n), r = n - 2 * g ;
if (r >= l)
res[x] += query(_rt[dfn[y] - 1], _rt[dfn[y] + sz[y] - 1], 1, n, l, r) ;
}
mdf(sz[x], 1) ;
for (auto k : edg[x])
if (k != fa) dfs5(k, x) ;
mdf(sz[x], -1) ;
}
void dfs6(int x, int fa, int qwq){//Case 3
int t = sz[x] + qwq ;
int r = n - 2 * sz[mson[x]] ;
int l = max(1, n - 2 * sz[x]) ;
if (r >= l)
res[x] += qry(l, r) ;
for (auto k : edg[x]){
if (k == fa) continue ;
mdf(t - sz[k], 1) ;
dfs6(k, x, t - sz[k]) ;
mdf(t - sz[k], -1) ;
}
}
int main(){
// freopen("centroid.in", "r", stdin) ;
// freopen("centroid.out", "w", stdout) ;
qr(T) ; int i, j, k ;
while (T --){
qr(n), ans = tot = 0 ;
for (i = 1 ; i <= n ; ++ i)
edg[i].clear(), mson[i] = res[i] = _bit[i] = 0 ;
for (i = 1 ; i < n ; ++ i)
qr(j), qr(k), edg[j].p_b(k), edg[k].p_b(j) ;
dfs4(1, Id = 0) ; build(_rt[0], 1, n) ;
for (i = 1 ; i <= n ; ++ i)
upd(_rt[i], _rt[i - 1], 1, n, sz[rev[i]]) ;
dfs5(1, 0) ; fill(_bit + 1, _bit + n + 1, 0) ; dfs6(1, 0, 0) ;
for (int i = 1 ; i <= n ; ++ i) ans += 1ll * res[i] * i ; qw(ans, '\n') ;
}
}
然后就做完了。复杂度是