题解 AT1219 【歴史の研究】

皎月半洒花

2020-04-13 18:03:10

Solution

大家好,我不喜欢回滚莫队,所以我用普通莫队把这题给 A 掉了。复杂度 $O(n\sqrt m + m\sqrt n)$。 考虑普通莫队的问题在哪。对于一个莫队而言,本身可以看作是 $O(n\sqrt m)$ 组查询,$O(m)$ 组询问,同时本身每个数的「出现次数带权」这个东西本身可以线段树来维护,所以复杂度可以看作是 $O(n\sqrt m\log n+ m\log n+m\log m)$ (最后一个 $\log$ 是排序的,当然也可以写基排不过显然不是很有必要)。 考虑复杂度重心向查询偏移,于是本着根号平衡的思想,大概需要一个 $O(\sqrt n)$ 查询,$O(1)$ 修改的 ds 来维护这个信息。但是似乎普通的值域分块不可做。 考虑问题出在哪:无法在当前最大值被 `del` 之后,快速找出下一个最大值。所以可以想到要去借鉴带一个 $\log $ 的权值线段树的做法,即假设某个 $x$ 的出现次数是 $1,2,3\cdots cnt_x$ ,那么一开始把所有的 $x\cdot 1,x\cdot 2, x\cdot 3\cdots,x\cdot cnt_x$ 放在一起离散化,然后对这个离散化后的数组进行值域分块。因为 $\sum _xcnt_x=n$ 所以可以知道复杂度是对的。 于是最后复杂度是 $O(n\sqrt m+m\sqrt n)$ 。因为滥用 `vector` 以及 cache 十分不友好导致慢的一匹,用了扶苏的 `fastI/O` 也毛用没有/dk。 ```cpp typedef long long ll ; #define il inline #define lwb lower_bound #define upb upper_bound #define vint vector <int> #define mint map <int, int> #define minttoll map <int, ll> using IPT::qr; using IPT::qra; using IPT::qrs; using IPT::qrdb; using OPT::qw; using OPT::qwa; using OPT::qws; il bool comp(const query &a, const query &b){ return (bl[a.l] ^ bl[b.l]) ? bl[a.l] < bl[b.l] : ((bl[a.l] & 1) ? a.r < b.r : a.r > b.r) ; } il void add(const int &p){ if (buc[tmp[p]] < 0){ ++ buc[tmp[p]] ; return ; } int lval = base[tmp[p]][buc[tmp[p]]] ; int val = base[tmp[p]][++ buc[tmp[p]]] ; // cout << val << " " << lval << endl ; sump[val] ++ ; sumb[ blv[val] ] ++ ; sump[lval] -- ; sumb[ blv[lval] ] -- ; } il void del(const int &p){ if (buc[tmp[p]] <= 0 ){ -- buc[tmp[p]] ; return ; } // if (buc[tmp[p]] + 1 >= base[tmp[p]].size()) cout << base <<buc[tmp[p]] << " " << p << ' ' << tmp[p] <<'\n', exit(0) ; int lval = base[tmp[p]][buc[tmp[p]]] ; int val = base[tmp[p]][-- buc[tmp[p]]] ; sump[val] ++ ; sumb[ blv[val] ] ++ ; sump[lval] -- ; sumb[ blv[lval] ] -- ; } il int ask(){ int ob = 0 ; for (int b = blv[V] ; b >= 0 ; -- b) if (sumb[b] > 0) { ob = b ; break ; } if (!ob) return 0 ; for (int p = vr[ob] ; p >= vl[ob] ; -- p) if (sump[p]) return p ; } int main(){ qr(n) ; qr(m) ; B = n / sqrt(m) ; for (int i = 1 ; i <= n ; ++ i) qr(g[i]) ; for (int i = 1 ; i <= n ; ++ i) bu[g[i]] ++, t[++ cnt] = 1ll * g[i] * bu[g[i]] ; // debug(t, 1, cnt, ' ', '\n') ; sort(t + 1, t + cnt + 1) ; bu.clear() ; for (int i = 1 ; i <= n ; ++ i){ bl[i] = i / B ; bu[g[i]] ++ ; int w ; w = upb(t + 1, t + cnt + 1, 1ll * g[i] * bu[g[i]]) - t - 1 ; // cout << g[i] << " " << w << '\n' ; if (bu[g[i]] == 1){ base[w].push_back(0) ; base[w].push_back(w) ; vu[g[i]] = w ; } else base[vu[g[i]]].push_back(w) ; V = max(V, w) ; su[w] = 1ll * g[i] * bu[g[i]] ; t[w] ++ ; } for (int i = 1 ; i <= m ; ++ i) qr(q[i].l), qr(q[i].r), q[i].id = i ; sort(q + 1, q + m + 1, comp) ; for (int i = 1 ; i <= n ; ++ i) tmp[i] = vu[g[i]] ; // for (int i = 1 ; i <= n ; ++ i) if(buc[tmp[i]]) return 0 ; else buc[tmp[i]] ++ ; /* for (int i = 1 ; i <= n ; ++ i){ cout << g[i] << " " << tmp[i] << '\n' ; debug(base[tmp[i]], 0, base[tmp[i]].size() - 1, ' ', '\n') ; }return 0 ; */ // debug(tmp, 1, n, ' ', '\n') ; B = sqrt(V) + 1 ; for (int i = 1 ; i <= V ; ++ i){ blv[i] = i / B + 1 ; if (blv[i] != blv[i - 1]) vr[blv[i - 1]] = i - 1, vl[blv[i]] = i ; } int l = 1, r = 0 ; vr[blv[V]] = V ; // debug(blv, 1, V, ' ', '\n') ; // debug(vl, 1, blv[V], ' ', '\n') ; // debug(vr, 1, blv[V], ' ', '\n') ; // while (l <= n) add(l ++) ;// cout << l << '\n' ; for (int i = 1 ; i <= m ; ++ i){ while (l > q[i].l) add(-- l) ; while (r < q[i].r) add(++ r) ; while (l < q[i].l) del(l ++) ; while (r > q[i].r) del(r --) ; ans[q[i].id] = su[ ask() ] ; } for (int i = 1 ; i <= m ; ++ i) qw(ans[i], '\n') ; return 0 ; } ```