• 维护前面的position+主席树 Codeforces Round #406 (Div. 2) E


    http://codeforces.com/contest/787/problem/E

    题目大意:给你n块,每个块都有一个颜色,定义一个k,表示在区间[l,r]中最多有k中不同的颜色。另k=1,2,3...n,问在每一种情况下,输出能划分出的最小的段落数。

    例如:

    5
    1 3 4 3 3
    ans = 4, 2, 1, 1, 1
    1. [1], [3], [4], [3, 3]
    2. [1], [3, 4, 3, 3]
    3. [1, 3, 4, 3, 3]
    4. [1, 3, 4, 3, 3]
    5. [1, 3, 4, 3, 3]

    思路:太久没写过主席树了,有点傻了

    先说这题的弱化版,只对于一个k=x的情况,统计能划分成几段

    其实这个问题,我们就只需要暴力一遍,定义lb=1,然后一直往后面暴力,for(rb; rb <=n; rb++)然后加入节点就用segment tree来维护。如果color的数目达到了k,我们就移动lb,让lb一直移动到等于rb,并在移动的同时删除线段树上的信息即可。所以复杂度是O(n*logn)

    那么对于每一个k,我们可以利用上面的思路来解决这个问题

    下面这一段来自这个人的:http://kugwzk.info/index.php/archives/2296

    首先不管怎么说,肯定是能尽量拿尽量拿,保证一个队尽可能的长。然后我们在枚举k的时候,其实已经有了一个O(nlogn)的复杂度了:因为n+n/2+n/3+....n/n=nlogn。。。所以我们必须要在logn的时间内找到对于一个位置i而言,最大的一个j,满足i到j的颜色数不超过k。那也就需要知道[i,j]这样一段区间有多少个不同的颜色。
    可以主席树维护一下这个东西,我们从位置1开始维护,用一个pre记录下来每种颜色的上一个位置在哪里。第i棵主席树表示的含义就是从1-i这样一段区间内的不同的颜色数目。也就是每个节点都存一下他所代表的区间内的颜色个数。因为我们记录下来了之前那种颜色的位置,所以新加入i位置的时候,在那个位置上减去1,在i位置上加上1.这样每次修改O(logn)个节点。从而满足统计出来1-i的颜色个数。

    然后我这里在主席树上面找的方法就是找还需要剩下多少颜色

    //看看会不会爆int!数组会不会少了一维!
    //取物问题一定要小心先手胜利的条件
    #include <bits/stdc++.h>
    using namespace std;
    #pragma comment(linker,"/STACK:102400000,102400000")
    #define LL long long
    #define ALL(a) a.begin(), a.end()
    #define pb push_back
    #define mk make_pair
    #define fi first
    #define se second
    #define haha printf("haha
    ")
    const int maxn = 1e5 + 5;
    int n;
    struct Tree{
        int lb, rb, val;
    }tree[maxn << 6];
    int pre[maxn], a[maxn], head[maxn];
    int k, tot;
    
    int update(int pos, int l, int r, int o, int cost){
        int k = ++tot;
        tree[k] = tree[o];
        tree[k].val += cost;
        if (l == pos && r == pos) return k;
        int mid = (l + r) / 2;
        if (pos <= mid) tree[k].lb = update(pos, l, mid, tree[o].lb, cost);
        if (pos > mid) tree[k].rb = update(pos, mid + 1, r, tree[o].rb, cost);
        return k;
    }
    
    int query(int l, int r, int o, int cost){
        if (l == r) return l;
        int mid = (l + r) / 2, lb = tree[o].lb, rb = tree[o].rb;
        if (tree[lb].val >= cost) return query(l, mid, lb, cost);
        return query(mid + 1, r, rb, cost - tree[lb].val);
    }
    
    int main(){
        cin >> n;
        for (int i = 1; i <= n; i++){
            scanf("%d", a + i);
            head[i] = update(i, 1, n, head[i - 1], 1);
            if (pre[a[i]]) head[i] = update(pre[a[i]], 1, n, head[i], -1);
            pre[a[i]] = i;
        }
        for (int i = 1; i <= n; i++){
            int ans = 0, pos = n;
            while (true){///因为要找i个不一样的颜色的,就要找个数为n-i个的
                int need = tree[head[pos]].val - i;
                if (need <= 0){ans++; break;}
                pos = query(1, n, head[pos], need);
                ans++;
            }
            printf("%d ", ans);
        }
        cout << endl;
    
        return 0;
    }
    View Code
  • 相关阅读:
    数组作为方法参数
    定义一个方法,根据商品总价,计算出对应的折扣并输出。折扣信息如下
    Cocos2d入门--1--初涉相关属性或代码
    JSP基础--JAVA遇见HTML
    查找算法--折半查找
    排序算法--冒泡排序
    排序算法--简单选择排序
    C语言的传值与传址调用
    学习C语言的数组
    如何获取QQ里的截图app?
  • 原文地址:https://www.cnblogs.com/heimao5027/p/6665294.html
Copyright © 2020-2023  润新知