zoukankan      html  css  js  c++  java
  • CF1039D You Are Given a Tree 根号分治、二分、贪心

    传送门


    似乎直接做不太好做……

    当你不会做的时候就可以考虑根号算法了(或许是这样的

    考虑如果只有一个询问如何计算答案。

    显然是可以贪心的,思路与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(nsqrt{n} + nsqrt{n}logn))的,还不够优秀。

    可以发现分治的两种计算的复杂度是不平均的,优化一下

    设小于等于(S)时暴力,大于(S)时二分,那么复杂度为(O(nS + n frac{n}{S} logn)),不难得到当(S= sqrt{nlogn})时有最优复杂度(O(nsqrt{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);
        for(int i = 2 ; i <= T ; ++i){
            solve(i);
            printf("%d
    " , 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
    " , cur);
            }
        }
    }
    
    int main(){
        input();
        init(1 , 0);
        work();
        return 0;
    }
    
  • 相关阅读:
    RDD 常用方法
    spark 机器学习
    java 读写文件内容
    hive datediff
    【转】[hadoop源代码解读] 【SequenceFile】
    【转】[Hadoop源码解读](六)MapReduce篇之MapTask类
    【转】[Hadoop源码解读](五)MapReduce篇之Writable相关类
    【转】[Hadoop源码解读](四)MapReduce篇之Counter相关类
    【转】[Hadoop源码解读](三)MapReduce篇之Job类
    【转】[Hadoop源码解读](二)MapReduce篇之Mapper类
  • 原文地址:https://www.cnblogs.com/Itst/p/10349283.html
Copyright © 2011-2022 走看看