校内训练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;
}

时间: 2024-08-06 10:47:50

校内训练0602 阿狸的统计学count的相关文章

校内训练0602 习题exercise

[题目大意] f(i)=((Af(i-1)+B)/(Cf(i-1)+D)) mod P. 给出f(0), A, B, C, D, P, n,求f(n). 多组数据T<=1e4 n<=1e18, P <= 1e9, |f(0)|,|A|,|B|,|C|,|D| <= 1e9 保证任何时候存在逆元. [题解] 首先我们有一种O(TP)的做法:找循环节. 考场上因为数据原因是可以AC的.. # include <map> # include <stdio.h> #

「csp校内训练 2019-10-24」解题报告

「csp校内训练 2019-10-24」解题报告 T1.猴猴吃苹果 \(Description\) 猴猴最喜欢在树上玩耍,一天猴猴又跳上了一棵树,这棵树有 \(N \ (N \leq 50000)\) 个苹果,每个苹果有一个编号,分别为 \(0\) ~ \(N - 1\) 它们之间由 \(N-1\) 个树枝相连,猴猴可以从树枝的一端爬到树枝的另一端,所以猴猴可以从任意一个苹果的位置出发爬到任意猴猴想去的苹果的位置. 猴猴开始在编号为 \(K \ (K < N)\) 的苹果的位置,并且把这个苹果吃

「csp校内训练 2019-10-30」解题报告

「csp校内训练 2019-10-30」解题报告 T1.树 题目链接(逃) \(Description\): 现在有一棵树,共 \(N\) 个节点. 规定:根节点为 \(1\) 号节点,且每个节点有一个点权. 现在,有 \(M\) 个操作需要在树上完成,每次操作为下列三种之一: \(1 \ x \ a\):操作 \(1\),将节点 \(x\) 点权增加 \(a\). \(2 \ x \ a\):操作 \(2\),将以节点 \(x\) 为根的子树中所有点的权值增加 \(a\). \(3 \ x\)

2017-4-7校内训练

丧病hzwer的ctsc训练赛 My AC:3/4 A.[Ctsc2014]企鹅QQ 思路:乱hash,我比较菜,写的丑代码各种WA+TLE,好久才A掉. #include<cstdio> #include<algorithm> using namespace std; #define ll long long #define MN 200 #define MX 6000000 #define MM 9000001 #define MOD1 890123798112473LL st

20170910校内训练

CCT 最近学校又发了n本五三题霸,BBS看到后十分高兴.但是,当他把五三拿到手后才发现,他已经刷过这些书了!他又认真地看了一会儿,发现新发的这些五三是2017版的,而他刷的是2016版的.现在他想找出所有他没有刷过的题来刷.每本五三都有m道题,并且它的特征(即它和去年版本的五三的差距)可以用一个m位二进制数来代表,二进制位上的1代表该题不同,0代表该题相同.比如4(100)就代表题目3和去年的有不同.5(101)就代表题目1和题目3和去年的有不同.而BBS热衷于给自己找麻烦,他要选择连续一段的

2017-3-3校内训练

hzwer出丧题虐人啦 ACM赛制 4/7 A.恼人的青蛙 题目大意:给定N*M矩阵上K个点,定义一条合法路径为从矩形外一点沿一条直线穿过矩形,每次走相同长度且在矩形内每步都要踩在给定点上,问经过给定点最多的路径经过几个点(若小于3输出0)(N,M,K<=5000). 思路:把点按横坐标第一关键字纵坐标第二关键字排序,f[i][j]表示有一条到i的路径,i上一个点是j,此时路径经过点数,每次确定i,j后就可以根据i,j算出j再前一个点的坐标,直接转移,复杂度O(K^2).评测机极慢稍微卡卡常才能

【三中校内训练】旅行

[题解] 显然的这是一道树形DP的题目 这里令f[i][0]为从i出发向以它为根的子树里走直到不能走的最大.最小价值 (不能走是什么自己阅读题目) 令s为x的儿子,w[i][j]为i和j之间的边的长度,则 f[x][0]=max(f[s][1]+w[s][i]) f[x][1]=min(f[s][0]+w[s][i]) 显然的通过这种方法,我们可以得到60分 可是,如何优化到线性呢 我们考虑一个节点x,x向某条边走的情况出现了很多次,浪费了很多时间 我们定义h[x][0/1]为从x出发,经过x的

校内训练0609 problem c

[题目大意] 给一棵树,求有多少条路径满足总和-最大值 是P的倍数 n<=10^5, P<=10^7 [题解] 一看就是点分治嘛 不考虑子树合并,考虑poj1741的做法,每次考虑经过重心的路径,用优先队列,从小到达添加并求答案即可. 容斥下. # include <queue> # include <stdio.h> # include <string.h> # include <iostream> # include <algorith

[3.10校内训练赛]

真的报警啦,hzwer又出一堆丧题虐人啦..... ------------------------------------------- A.[poj-1190]生日蛋糕 要做一个m层的蛋糕,每一层有高度和半径,且要分别比它上面的那一层的高度和半径大至少一,给定总体积n,求最小的侧面和顶上的面积之和m<=20,n<=10000 搜索....但是要加上比较强的剪枝. 1.如果此时的半径和高度无法建出剩余体积那么大的蛋糕,剪掉.这种情况我们不考虑半径和高度的减小,直接用((r-1)^2+(h-1