[BZOJ3236][Ahoi2013]作业(莫队+树状数组)
2018-01-04 20:05
274 查看
此题的询问是一个位置和权值都有限制的二维区间,但是题目具备无修改和允许离线两个条件,可以用莫队算法解决。
一个想法是:用莫队维护位置,树状数组维护权值。
具体的说,用一个数组cnt,维护莫队当前到达的位置区间内每个值的出现次数,并用两个树状数组,一个维护cnt的前缀和,另一个维护对于所有的j∈[1,i],cnt[j]>0的个数。
莫队移动指针时,以位置区间[l,r]转移到[l,r+1]为例:
记位置r+1上的值为x,则cnt[x]加一,并且在第一个树状数组的第x个位置加1。此时,还要判断如果现在cnt[x]=1,则第二个树状数组的第x个位置也要加1。
这样,第一个树状数组的区间[a,b]之和就是第一问的结果,第二个树状数组的区间[a,b]之和就是第二问的结果。
复杂度O((m+n)n−−√logn。此外,还有一种O((m+n)n−−√)的神奇的分块做法……
代码:
一个想法是:用莫队维护位置,树状数组维护权值。
具体的说,用一个数组cnt,维护莫队当前到达的位置区间内每个值的出现次数,并用两个树状数组,一个维护cnt的前缀和,另一个维护对于所有的j∈[1,i],cnt[j]>0的个数。
莫队移动指针时,以位置区间[l,r]转移到[l,r+1]为例:
记位置r+1上的值为x,则cnt[x]加一,并且在第一个树状数组的第x个位置加1。此时,还要判断如果现在cnt[x]=1,则第二个树状数组的第x个位置也要加1。
这样,第一个树状数组的区间[a,b]之和就是第一问的结果,第二个树状数组的区间[a,b]之和就是第二问的结果。
复杂度O((m+n)n−−√logn。此外,还有一种O((m+n)n−−√)的神奇的分块做法……
代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 1e5 + 5, M = 1e6 + 5; int n, m, sq, a , S , T , cnt , res1[M], res2[M]; struct cyx {int id, l, r, a, b, bl;} que[M]; bool comp(cyx a, cyx b) { if (a.bl != b.bl) return a.bl < b.bl; return a.r < b.r; } int lowbit(int x) {return x & -x;} void changeS(int x, int v) { for (; x <= n; x += lowbit(x)) S[x] += v; } int askS(int x) { int res = 0; for (; x; x -= lowbit(x)) res += S[x]; return res; } void changeT(int x, int v) { for (; x <= n; x += lowbit(x)) T[x] += v; } int askT(int x) { int res = 0; for (; x; x -= lowbit(x)) res += T[x]; return res; } inline int read() { int res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + (c - 48); return bo ? ~res + 1 : res; } int main() { int i; n = read(); m = read(); sq = sqrt(n); for (i = 1; i <= n; i++) a[i] = read(); for (i = 1; i <= m; i++) { que[i].l = read(); que[i].r = read(); que[i].a = read(); que[i].b = read(); que[i].id = i; que[i].bl = (que[i].l - 1) / sq + 1; } sort(que + 1, que + m + 1, comp); for (i = que[1].l; i <= que[1].r; i++) { changeS(a[i], 1); cnt[a[i]]++; if (cnt[a[i]] == 1) changeT(a[i], 1); } res1[que[1].id] = askS(que[1].b) - askS(que[1].a - 1); res2[que[1].id] = askT(que[1].b) - askT(que[1].a - 1); for (i = 2; i <= m; i++) { int tl = que[i - 1].l, tr = que[i - 1].r, vl = que[i].l, vr = que[i].r; while (tl > vl) { changeS(a[--tl], 1); cnt[a[tl]]++; if (cnt[a[tl]] == 1) changeT(a[tl], 1); } while (tr < vr) { changeS(a[++tr], 1); cnt[a]++; if (cnt[a ] == 1) changeT(a , 1); } while (tl < vl) { changeS(a[tl++], -1); cnt[a[tl - 1]]--; if (!cnt[a[tl - 1]]) changeT(a[tl - 1], -1); } while (tr > vr) { changeS(a[tr--], -1); cnt[a[tr + 1]]--; if (!cnt[a[tr + 1]]) changeT(a[tr + 1], -1); } res1[que[i].id] = askS(que[i].b) - askS(que[i].a - 1); res2[que[i].id] = askT(que[i].b) - askT(que[i].a - 1); } for (i = 1; i <= m; i++) printf("%d %d\n", res1[i], res2[i]); fclose(stdin); fclose(stdout); return 0; } 相关文章推荐
- [bzoj3236][Ahoi2013]作业(莫队+树状数组)
- BZOJ 3236: [Ahoi2013]作业|莫队算法|树状数组
- bzoj 3236: [Ahoi2013]作业 (莫队+分块)
- [BZOJ 3236] [Ahoi2013] 作业 && [BZOJ 3809] 【莫队(+分块)】
- [BZOJ3236][Ahoi2013]作业(莫队+分块)
- 【莫队算法】【权值分块】bzoj3236 [Ahoi2013]作业
- 【BZOJ3809/3236】Gty的二逼妹子序列 [Ahoi2013]作业 莫队算法+分块
- 【BZOJ3236】【AHOI2013】作业 线段树 分治 树状数组
- BZOJ 3809: Gty的二逼妹子序列 & 3236: [Ahoi2013]作业 [莫队]
- BZOJ_3809_Gty的二逼妹子序列 && BZOJ_3236_[Ahoi2013]作业 _莫队+分块
- Bzoj 3236: [Ahoi2013]作业 莫队,分块
- BZOJ3236 [Ahoi2013]作业 【莫队 + 树状数组】
- BZOJ3236: [Ahoi2013]作业 莫队+分块
- bzoj 3236: [Ahoi2013]作业 莫队算法+分块
- BZOJ 3236: [Ahoi2013]作业( 莫队 + BIT )
- BZOJ 3236 AHOI 2013 作业 莫队算法
- BZOJ 3236: [Ahoi2013]作业
- bzoj 3236: [Ahoi2013]作业
- 【BZOJ 3236】 [Ahoi2013]作业
- BZOJ 3236: [Ahoi2013]作业