1. 程式人生 > >CF1039D You Are Given a Tree 根號分治、二分、貪心

CF1039D You Are Given a Tree 根號分治、二分、貪心

繼續 nlogn code spa 分治 得到 復雜 ini class

傳送門


似乎直接做不太好做……

當你不會做的時候就可以考慮根號算法了(或許是這樣的

考慮如果只有一個詢問如何計算答案。

顯然是可以貪心的,思路與NOIP2018D1T3是相同的。每一個點向上傳一條鏈,對於某一個點,如果從兒子傳上來的所有鏈中最長的兩條的長度之和\(\geq k\)就連上,否則就把其中最長的那一條傳上去。

然後考慮所有詢問。

可以發現:對於鏈長\(>\sqrt{n}\)的所有詢問,最多只有\(\sqrt{n}\)種答案。

所以對於鏈長\(\leq \sqrt{n}\)的詢問暴力計算

對於鏈長\(> \sqrt{n}\)的詢問,因為答案隨著鏈長增加單調不降,所以可以二分。設當前計算到了\(j\)

,先算出\(j\)的答案,然後二分出答案與\(j\)相等的最大的\(k\),那麽對於\(\forall i \in [j,k]\),鏈長為\(i\)的答案都相等,輸出\(k-j+1\)次當前計算出的答案,然後繼續計算\(k+1\)

這個算法的復雜度是\(O(n\sqrt{n} + n\sqrt{n}logn)\)的,還不夠優秀。

可以發現分治的兩種計算的復雜度是不平均的,優化一下

設小於等於\(S\)時暴力,大於\(S\)時二分,那麽復雜度為\(O(nS + n \frac{n}{S} logn)\),不難得到當\(S= \sqrt{nlogn}\)時有最優復雜度\(O(n\sqrt{nlogn})\)

註意:計算某一種鏈長的答案不要使用遞歸,應先處理好拓撲序然後遞推,這樣可以大大加快程序運行速度。

#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == ‘-‘)
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ ‘0‘);
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 1e5 + 10;
struct Edge{
    int end , upEd;
}Ed[MAXN << 1];
int head[MAXN] , cur[MAXN] , top[MAXN] , fa[MAXN] , N , T , cntEd , ans , ts;

inline void addEd(int a , int b){
    Ed[++cntEd].end = b;
    Ed[cntEd].upEd = head[a];
    head[a] = cntEd;
}

void input(){
    N = read();
    T = sqrt(N * log2(N));
    for(int i = 1 ; i < N ; ++i){
        int a = read() , b = read();
        addEd(a , b);
        addEd(b , a);
    }
}

void init(int x , int p){
    fa[x] = p;
    top[++ts] = x;
    for(int i = head[x] ; i ; i = Ed[i].upEd)
        if(Ed[i].end != p)
            init(Ed[i].end , x);
}

void solve(int q){
    ans = 0;
    fill(cur + 1 , cur + N + 1 , 1);
    for(int i = N ; i > 1 ; --i){
        int x = top[i];
        if(cur[fa[x]] != -1 && cur[x] != -1){
            if(cur[fa[x]] + cur[x] >= q){
                cur[fa[x]] = -1;
                ++ans;
            }
            else
                cur[fa[x]] = max(cur[fa[x]] , cur[x] + 1);
        }
    }
}

void work(){
    printf("%d\n" , N);
    for(int i = 2 ; i <= T ; ++i){
        solve(i);
        printf("%d\n" , ans);
    }
    for(int j = T + 1 ; j <= N ; ){
        solve(j);
        int cur = ans , L = j , R = N;
        while(L < R){
            int mid = (L + R + 1) >> 1;
            solve(mid);
            if(ans == cur)
                L = mid;
            else
                R = mid - 1;
        }
        while(j <= L){
            ++j;
            printf("%d\n" , cur);
        }
    }
}

int main(){
    input();
    init(1 , 0);
    work();
    return 0;
}

CF1039D You Are Given a Tree 根號分治、二分、貪心