洛谷 2056 采花 - 可持久化线段数 - 树状数组

Posted 阿波罗2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 2056 采花 - 可持久化线段数 - 树状数组相关的知识,希望对你有一定的参考价值。

莫队做法请参见原来的博客 [传送门]

Solution II (主席树,在线)

  考虑直接利用主席树统计每个位置对答案的贡献。

  查询[l, r]就是在第r棵线段树内查询[l, r]

  对于建树,考虑到位置i,和位置(i - 1)的不同在于,位置i可能会导致之前的某个位置对答案的贡献从1变为0,或者某个位置的贡献从0变为1.

  至于什么什么情况产生贡献?考虑在[1, r]中,从r向1计数,某个数第二出现的位置就对答案有1的贡献。

  然后建树的过程就很显然了。

Code

  1 /**
  2  * luogu
  3  * Problem#2056
  4  * Accepted
  5  * Time: 736ms
  6  * Memory: 88007k
  7  */ 
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 typedef pair<int, int> pii;
 12 #define fi first
 13 #define sc second
 14 
 15 typedef class SegTreeNode {
 16     public:
 17         int val;
 18         SegTreeNode *l;
 19         SegTreeNode *r;
 20         int belong;
 21         
 22         SegTreeNode(int val = 0):val(val), l(NULL), r(NULL), belong(-1) {        }
 23         SegTreeNode(int val, SegTreeNode* org):val(val), l(org), r(org), belong(-1) {        }
 24         
 25         void pushUp() {
 26             val = l->val + r->val;
 27         }
 28 }SegTreeNode;
 29 
 30 #define Limit 2000000
 31 
 32 SegTreeNode pool[Limit];
 33 SegTreeNode* top = pool;
 34 SegTreeNode null = SegTreeNode(0, &null);
 35 
 36 SegTreeNode* newnode() {
 37     if(top == pool + Limit)
 38         return new SegTreeNode(0, &null);
 39     *top = null;
 40     return top++; 
 41 }
 42 
 43 typedef class SegTree {
 44     public:
 45         int ver;
 46         int n;
 47         SegTreeNode** rt;
 48         
 49         SegTree() {    }
 50         SegTree(int n):ver(0), n(n) {
 51             rt = new SegTreeNode*[(n + 1)];
 52             for(int i = 0; i <= n; i++)
 53                 rt[i] = &null;
 54         }
 55         
 56         void update(SegTreeNode*& pOld, SegTreeNode*& pNew, int l, int r, int p, int val) {
 57             if(pNew->belong != ver + 1)
 58                 pNew = newnode(), *pNew = *pOld, pNew->belong = ver + 1;
 59             pNew->val += val;
 60             if(l == r)
 61                 return;
 62             int mid = (l + r) >> 1;
 63             if(p <= mid)
 64                 update(pOld->l, pNew->l, l, mid, p, val);
 65             else
 66                 update(pOld->r, pNew->r, mid + 1, r, p, val);
 67         }
 68         
 69         int query(SegTreeNode*& node, int l, int r, int ql, int qr) {
 70             if(l == ql && r == qr)
 71                 return node->val;
 72             int mid = (l + r) >> 1, rt = 0;
 73             if(ql <= mid)
 74                 rt += query(node->l, l, mid, ql, (qr < mid) ? (qr) : (mid));
 75             if(qr > mid)
 76                 rt += query(node->r, mid + 1, r, (ql > mid) ? (ql) : (mid + 1), qr);
 77             return rt;
 78         }
 79         
 80         SegTreeNode*& operator [] (int pos) {
 81             return rt[pos];
 82         }
 83 }SegTree;
 84 
 85 int n, c, m;
 86 SegTree st;
 87 int *ar;
 88 
 89 inline void init() {
 90     scanf("%d%d%d", &n, &c, &m);
 91     st = SegTree(n);
 92     ar = new int[(n + 1)];
 93     for(int i = 1; i <= n; i++)
 94         scanf("%d", ar + i);
 95 }
 96 
 97 pii *last;
 98 inline void solve() {
 99     last = new pii[(c + 1)];
100     fill(last + 1, last + c + 1, pii(-1, -1));
101     for(int i = 1; i <= n; i++) {
102         boolean aflag = true;
103         if(last[ar[i]].sc != -1)
104             st.update(st[i - 1], st[i], 1, n, last[ar[i]].sc, -1), aflag = false;
105         last[ar[i]].sc = last[ar[i]].fi;
106         last[ar[i]].fi = i;
107         if(last[ar[i]].sc != -1)
108             st.update(st[i - 1], st[i], 1, n, last[ar[i]].sc, 1), aflag = false;
109         if(aflag)
110             st[i] = st[i - 1];
111         st.ver++;
112     }
113     
114     int l, r;
115     while(m--) {
116         scanf("%d%d", &l, &r);
117         printf("%d\\n", st.query(st[r], 1, n, l, r));
118     }
119 }
120 
121 int main() {
122     init();
123     solve();
124     return 0;
125 }
Solution II (主席树)

Solution III (树状数组,离线)

  结合一下莫队算法再想想,其实可以读入所有操作,按照右端点排序,在建树的过程中就可以计算答案。

  因为没必要把之前的树保留,所以直接用树状数组就好了。

Code

 1 /**
 2  * luogu            & codevs
 3  * Problem#2056        & 2365
 4  * Accepted            & Accepted
 5  * Time: 188ms        & 3500ms
 6  * Memory: 4457k    & 53772k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 typedef bool boolean;
11 typedef pair<int, int> pii;
12 #define fi first
13 #define sc second
14 
15 typedef class IndexedTree {
16     public:
17         int s;
18         int* ar;
19         
20         IndexedTree() {        }
21         IndexedTree(int n):s(n) {
22             ar = new int[(n + 1)];
23             memset(ar, 0, sizeof(int) * (n + 1));
24         }
25         
26         inline void add(int idx, int val) {
27             for(; idx <= s; idx += (idx & (-idx)))
28                 ar[idx] += val;
29         }
30         
31         inline int getSum(int idx) {
32             int rt = 0;
33             for(; idx; idx -= (idx & (-idx)))
34                 rt += ar[idx];
35             return rt;
36         }
37 }IndexedTree;
38 
39 typedef class Query {
40     public:
41         int l, r, id;
42         
43         Query(int l = 0, int r = 0, int id = 0):l(l), r(r), id(id) {        }
44         
45         boolean operator < (Query b) const {
46             if(r != b.r)    return r < b.r;
47             return l < b.l;
48         }
49 }Query;
50 
51 int n, c, m;
52 Query *qs;
53 int* ar;
54 IndexedTree it;
55 
56 inline void init() {
57     scanf("%d%d%d", &n, &c, &m);
58     it = IndexedTree(n);
59     qs = new Query[(m + 1)];
60     ar = new int[(n + 1)];
61     for(int i = 1; i <= n; i++)
62         scanf("%d", ar + i);
63     for(int i = 1; i <= m; i++)
64         scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i;
65 }
66 
67 pii *last;
68 int *res;
69 inline void solve() {
70     last = new pii[(c + 1)];
71     res = new int[(m + 1)];
72     fill(last + 1, last + c + 1, pii(-1, -1));
73     sort(qs + 1, qs + m + 1);
74     int p = 1;
75     for(int i = 1, e; i <= n && p <= m; i++) {
76         e = ar[i];
77         if(last[e].sc != -1)
78             it.add(last[e].sc, -1);
79         last[e].sc = last[e].fi;
80         last[e].fi = i;
81         if(last[e].sc != -1)
82             it.add(last[e].sc, 1);
83         while(qs[p].r == i)
84             res[qs[p].id] = it.getSum(qs[p].r) - it.getSum(qs[p].l - 1), p++;
85     }
86     for(int i = 1; i <= m; i++)
87         printf("%d\\n", res[i]);
88 }
89 
90 int main() {
91     init();
92     solve();
93     return 0;
94 }

以上是关于洛谷 2056 采花 - 可持久化线段数 - 树状数组的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P2056 采花 - 莫队算法

洛谷 P2056 BZOJ 2743 [HEOI2012]采花

洛谷P2056 采花

AC日记——采花 洛谷 P2056

luogu P2056 采花

[洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?