校內訓練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; typedefView Codelong 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; }
校內訓練0602 阿貍的統計學count