首页 > 代码库 > 校内训练0602 阿狸的统计学count

校内训练0602 阿狸的统计学count

【题目大意】

一个数列a[]有n个数,m次操作:

1 l r x:将a[l...r]都改成x

2 l r:求a[l...r]中数在当前区间出现率>=p%的数,为了方便做题,你可以输出k个数,满足k*p<=100,如果k个数中完全包含了答案,那么就判为正确。

1<=n,m,a[i],x<=150000, 20<=p<=100

【题解】

考虑之前做过的一个题:有一个数在区间中出现了>50%,求这个数:做法是我随便找出2个不同的数消去,到不能消的时候,最后剩下的那个数就是答案。

考虑出现了>=50%,这时候就不能按照上面那么做了,因为=50%的时候消去可能会全消完?那怎么做?随便找3个不同的数消去,等到消不动的时候,要么是还剩一个数,就是答案了,要么是还剩两个数,可能2个数都是答案(都出现了50%),或者其中1个数是答案。

下面的记号:[x]表示x的整数部分。

考虑推广?有一个数在区间中出现了>=p%,我随便找[100/p]+1个不同的数消去,最后剩下[100/p]个数,满足题目要求,且这一定包含答案集合。

考虑用线段树来维护这个序列:

这棵线段树的每个节点存着小于等于[100/p]个数及他们出现次数,代表我这个区间消剩下的数。

考虑合并两个区间,设节点为a和b,可以暴力合并。

假装a的答案就是答案,考虑用b的每一个答案来更新:

如果a没有[100/p]个数那么直接把b的这个答案装进去;

如果a有这个数,显然可以把答案合并。

否则,找出a中出现次数最小的数,设其出现次数为times,b的这个答案的出现次数times‘。

如果times<times‘,显然把b的这个答案替换a中出现次数最小的数,然后就可以把a中的所有数的出现次数减去times(同时消去times次[100/p]+1个数)

否则,显然保留a中的那个数更优,a中的所有数的出现次数减去times‘(同时消去times‘次[100/p]+1个数)

很明显上述过程不可能造成某个数出现次数变为负数,所以均合法。

然后就支持区间合并了,用线段树来维护即可。

技术分享
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 150000 + 10, N = 7;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, p, A[M], Max;

struct node {
    int p[N], t[N], n;
    friend node operator + (node a, node b) {
        node ret; ret.n = a.n;
        for (int i=1; i<=ret.n; ++i) ret.p[i] = a.p[i], ret.t[i] = a.t[i];
        for (int i=1; i<=b.n; ++i) {
            bool ok = 0;
            for (int j=1; j<=ret.n; ++j) {
                if(ret.p[j] == b.p[i]) {
                    ret.t[j] += b.t[i];
                    ok = 1;
                    break;
                }
            }
            if(ok) continue;
            if(ret.n < Max) {
                ++ret.n;
                ret.p[ret.n] = b.p[i], ret.t[ret.n] = b.t[i];
                continue;
            }
            int pos = 1;
            for (int j=2; j<=ret.n; ++j) 
                if(ret.t[j] < ret.t[pos]) pos = j;
            if(ret.t[pos] >= b.t[i]) {
                for (int j=1; j<=ret.n; ++j)
                    ret.t[j] -= b.t[i];
            } else {
                int times = ret.t[pos];
                ret.p[pos] = b.p[i];
                ret.t[pos] = b.t[i];
                for (int j=1; j<=ret.n; ++j) 
                    ret.t[j] -= times;
            } 
        }
        return ret;
    }            
};

struct SMT {
    node w[M * 4];
    int tag[M * 4];
    # define ls (x<<1)
    # define rs (x<<1|1)
    inline void pushtag(int x, int d, int l, int r) {
        if(!x) return;
        w[x].n = 1;
        w[x].p[1] = d;
        w[x].t[1] = r-l+1;
        tag[x] = d;
    }
    inline void down(int x, int l, int r) {
        if(!x) return ;
        if(tag[x] == 0) return; 
        int mid = l+r>>1;
        pushtag(ls, tag[x], l, mid);
        pushtag(rs, tag[x], mid+1, r);
        tag[x] = 0;
    }
    inline void edt(int x, int l, int r, int L, int R, int d) {
        if(L <= l && r <= R) {
            pushtag(x, d, l, r);
            return ;
        }
        down(x, l, r);
        int mid = l+r>>1;
        if(L <= mid) edt(ls, l, mid, L, R, d);
        if(R > mid) edt(rs, mid+1, r, L, R, d);
        w[x] = w[ls] + w[rs];
    }
    inline node query(int x, int l, int r, int L, int R) {
        if(L <= l && r <= R) return w[x];
        down(x, l, r);
        int mid = l+r>>1;
        if(R <= mid) return query(ls, l, mid, L, R);
        else if(L > mid) return query(rs, mid+1, r, L, R);
        else return query(ls, l, mid, L, mid) + query(rs, mid+1, r, mid+1, R);
    }
}T;

int main() {
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);
    cin >> n >> m >> p;
    Max = 100/p;
    for (int i=1; i<=n; ++i) {
        scanf("%d", &A[i]);
        T.edt(1, 1, n, i, i, A[i]);
    }
    int op, l, r, x;
    node t;
    while(m--) {
        scanf("%d%d%d", &op, &l, &r);
        if(op == 1) {
            scanf("%d", &x);
            T.edt(1, 1, n, l, r, x);
        } else {
            t = T.query(1, 1, n, l, r);
            printf("%d", t.n);
            for (int i=1; i<=t.n; ++i) 
                printf(" %d", t.p[i]);
            puts("");
        }
    }
    return 0;
}
View Code

 

校内训练0602 阿狸的统计学count