• CF786C Till I Collapse(根号分治)题解


    题意

    (n)个数划分成(m)段使得每中不同数字的个数(le k),对于每个(k)满足(1le kle n)求出最小的(m)

    算法

    根号分治+二分

    思路

    面对那不大不小的数据范围,难以优化的查询方法以及令人疑惑的值域,我们可以联想到根号分治。

    首先明确:

    • 答案的范围是不会超过(frac{n}{k})的。(就算全都不一样也只要这么多次划分)
    • (k)单调递增时,答案单调不减,即答案具有二分性质

    根据第一条性质,我们发现,不同答案的个数是不超过(sqrt{n})级别的,而结合第二条性质,答案相同的(k)一定是连续的。

    所以我们得出一下做法:

    • 对于(k le sqrt{n})的情况:直接暴击(O(n))统计;

    • 对于(k > sqrt{n})的情况:也是暴力统计,但统计完以后,需要二分出与当前答案相同的区间,将这段区间的答案一起输出。

    参考代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int maxn = 1e5 + 10;
    int n,a[maxn],Ans,siz;
    bool T[maxn];
    
    int work(int k){
        memset(T,0,sizeof(T));
        int sum = 0, cnt = 0, sta = 1;
        for(int i = 1; i <= n; ++ i){
            if(!T[a[i]]) T[a[i]] = 1, cnt ++;
            if(cnt > k){
                sum++, cnt = 1;
                for(int j = sta; j < i; ++ j) T[a[j]] = 0;
                T[a[i]] = 1; sta = i;
            }
        }
        if(cnt) sum++;
        return sum;
    }
    
    int main(){
        scanf("%d", &n); siz = sqrt(n);
        for(int i = 1; i <= n; ++ i) scanf("%d", a + i);
        for(int k = 1; k <= n; ++ k){
            if(k <= siz) printf("%d ", work(k));
            else{
                Ans = work(k);
                int l = k, r = n, mid;
                while(l <= r){
                    mid = (l + r) >> 1;
                    if(work(mid) < Ans) r = mid - 1;
                    else l = mid + 1;
                }
                for(int i = k; i <= l - 1; ++ i) printf("%d ", Ans);
                k = l - 1;
            }
        }
        printf("
    ");
        return 0;
    }
    
  • 相关阅读:
    Linux下修改Tomcat默认端口
    java 中 byte[]、File、InputStream 互相转换
    安装mule-standalone说明
    python: 可变参数
    vim编码方式设置
    ASCII, Unicode 与 UTF-8
    Vim: 强大的g
    Vim模糊查找与替换
    Vim统计字符串出现次数
    APB简介
  • 原文地址:https://www.cnblogs.com/whenc/p/14057744.html
Copyright © 2020-2023  润新知