【十二省联考 2019】异或粽子 解题报告

菜啊,不会 100 pts,但是 zhq 讲得真好


先看另一道题:luogu P1631 序列合并

简述题意:两个长度为 n 的单调不下降序列 A, B,在 A, B 中各取一个数相加,共有 $n^2$ 种可能,求最小的 n 个

数据范围:$n\leq 10^5$

将所有可能是答案的数放进一个小根堆中,贪心地想,如果一对数 $A_i, B_j$ 被还不是当前堆最小的一对数,那么 $A_{i+1}, B_j$ 或 $A_i, B_{j + 1}$ 也不可能是答案(因为序列单调不下降),而当 $A_i, B_j$ 被当作第 k 个答案取出的时候,则 $A_{i+1}, B_{j}$ 和 $A_i, B_{j+1}$ 也就有可能作为第 k+1 个答案,所以将这两个加入堆中

以这样的思路完成:

 1 // P1631 序列合并
 2 
 3 #include <ctime>
 4 #include <cmath>
 5 #include <cstdio>
 6 #include <cstring>
 7 #include <cstdlib>
 8 #include <iostream>
 9 #include <algorithm>
10 #include <vector>
11 #include <queue>
12 #define inf 100010
13 #define INF 0x7fffffff
14 #define ll long long
15 
16 namespace chiaro{
17 
18 template <class I>
19 inline void read(I &num){
20     num = 0; char c = getchar(), up = c;
21     while(c < '0' || c > '9') up = c, c = getchar();
22     while(c >= '0' && c <= '9') num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
23     up == '-' ? num = -num : 0; return;
24 }
25 template <class I>
26 inline void read(I &a, I &b) {read(a); read(b);}
27 template <class I>
28 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
29 
30 struct Node {
31     int i, j;
32     int dis;
33     inline bool operator < (const Node& other) const {
34         return dis > other.dis;
35     }
36     Node() {i = j = dis = 0;}
37     Node(int a, int b, int c) {i = a, j = b, dis = c;}
38 };
39 
40 int n;
41 int a[inf], b[inf];
42 std::priority_queue <Node, std::vector <Node> > P;
43 
44 inline int main(){
45     read(n);
46     for(int i = 1; i <= n; i++) read(a[i]);
47     for(int i = 1; i <= n; i++) read(b[i]);
48     P.push((Node){1, 1, a[1] + b[1]});
49     for(int i = 1; i <= n; i++) {
50         Node x = P.top(); P.pop();
51         printf("%d ", x.dis);
52         P.push((Node){x.i, x.j + 1, a[x.i] + b[x.j + 1]});
53         if(x.j == 1) P.push((Node){x.i + 1, x.j, a[x.i + 1] + b[x.j]});
54     }
55     return 0;
56 }
57 
58 }
59 
60 signed main(){ return chiaro::main();}
序列合并

再看另一道题:【NOI2010】超级钢琴

简述题意:给定一个长度为 n 的序列,要求从中找去 k 个不同区间(区间大小不小于 L, 不大于 R),将 k 个区间全部求和,要求和最大

数据范围:$n,k\leq 500000$

显然需要维护一个前缀和 sum[] ,做法与上题相似

我们现在钦定一个点 x 作为左端点,那么右端点就大致在 $[x + L - 1, x + R - 1]$ 中,那么我们其实就是要在这个区间内找一个 sum 最大的点

区间 RMQ 问题,没有修改,上 ST 表

当我们找到了一个答案后 $(x, y)$,那么有哪些可能使下一个答案?

还是让左端点 x 固定,则答案存在的区间被 y 点割成了两个部分,即 $[x + L - 1, y - 1]$ 与 $[y + 1, x + R - 1]$

就将这两个区间 ST 求完最大值后加入堆

 1 // P2048 [NOI2010]超级钢琴
 2 
 3 #include <ctime>
 4 #include <cmath>
 5 #include <cstdio>
 6 #include <cstring>
 7 #include <cstdlib>
 8 #include <iostream>
 9 #include <algorithm>
10 #include <vector>
11 #include <queue>
12 #define inf 500010
13 #define INF 0x7fffffff
14 #define ll long long
15 
16 namespace chiaro{
17 
18 template <class I>
19 inline void read(I &num){
20     num = 0; char c = getchar(), up = c;
21     while(c < '0' || c > '9') up = c, c = getchar();
22     while(c >= '0' && c <= '9') num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
23     up == '-' ? num = -num : 0; return;
24 }
25 template <class I>
26 inline void read(I &a, I &b) {read(a); read(b);}
27 template <class I>
28 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
29 
30 struct Node {
31     int x;
32     int l, r;
33     int p;
34     Node() {x = l = r = p = 0;}
35     Node(int a, int b, int c, int d) {x = a, l = b, r = c, p = d;}
36 };
37 
38 int n, k;
39 int L, R;
40 int a[inf];
41 ll sum[inf];
42 
43 ll table[24][inf];
44 int log[inf];
45 
46 std::priority_queue <Node, std::vector <Node> > P;
47 
48 inline bool operator < (const Node& now, const Node& other) {
49     return (sum[now.p] - sum[now.x - 1]) < (sum[other.p] - sum[other.x - 1]);
50 }
51 
52 #define max(a, b) (sum[(a)] > sum[(b)] ? (a) : (b))
53 
54 inline void build(int n) {
55     for(int i = 2; i <= n; i++) log[i] = log[i >> 1] + 1;
56     for(int i = 1; i <= n; i++) table[0][i] = i;
57     for(int j = 1; (1 << j) <= n; j++) {
58         for(int i = 1; i + (1 << j) - 1 <= n; i++) {
59             int x = table[j - 1][i];
60             int y = table[j - 1][i + (1 << (j - 1))];
61             table[j][i] = max(x, y);
62         }
63     }
64 }
65 
66 inline int query(int l, int r) {
67     int k = log[r - l + 1];
68     int x = table[k][l];
69     int y = table[k][r - (1 << k) + 1];
70     return max(x, y);
71 }
72 
73 inline int main(){
74     read(n, k); read(L, R);
75     for(int i = 1; i <= n; i++) read(a[i]);
76     for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
77     build(n);
78     for(int i = 1; i <= n; i++) {
79         if(i + L - 1 > n) break;
80         int l = i + L - 1;
81         int r = std::min (n, i + R - 1);
82         P.push((Node){i, l, r, query(l, r)});
83     }
84     ll ans = 0;
85     while(k--) {
86         Node x = P.top(); P.pop();
87         ans += sum[x.p] - sum[x.x - 1];
88         if(x.p != x.l) P.push((Node){x.x, x.l, x.p - 1, query(x.l, x.p - 1)});
89         if(x.p != x.r) P.push((Node){x.x, x.p + 1, x.r, query(x.p + 1, x.r)});
90     }
91     printf("%lld\n", ans);
92     return 0;
93 }
94 
95 }
96 
97 signed main(){ return chiaro::main();}
超级钢琴

做完上述两题后就来看这道题:【十二省联考2019】异或粽子

简述题意:给定长度为 n 的数列,要求找 k 个不同的区间,将区间内的数全部亦或,然后将 k 区间的答案求和,要求和最大

zhq:看到亦或和最大就应该想到 Trie

我做题少不要欺负我

与上题相似的维护前缀亦或 sum[]

与上题相似,不过为了下文方便我们固定右端点 x,然后在 $[0, x - 1]$ 中找一个点 y,使得 $sum[x]\ xor\ sum[y]$ 最大可用 Trie 轻易维护

但是我们要固定找的范围(即 $[0, x - 1]$),所以需要用可持久化 Trie 

可是我不会可持久化 Trie

那就不固定范围,可以发现对于一对答案点 $(x,y)$, 当 x 被固定的时候会最先找到 y 点, 当 y 被固定的时候 x 最先被找到,所以一对点一定且只会被找到两次

又因为 $sum[x]\ xor\ sum[y] = sum[y]\ xor\ sum[x]$,那么我们不妨找 2k 个答案,然后将最后答案除以二

代码:

 1 #include <ctime>
 2 #include <cmath>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <iostream>
 7 #include <algorithm>
 8 #include <vector>
 9 #include <queue>
10 #define inf 500010
11 #define INF 0x7fffffff
12 #define ll long long
13 #define int unsigned int
14 
15 namespace chiaro{
16 
17 template <class I>
18 inline void read(I &num){
19     num = 0; char c = getchar(), up = c;
20     while(c < '0' || c > '9') up = c, c = getchar();
21     while(c >= '0' && c <= '9') num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
22     up == '-' ? num = -num : 0; return;
23 }
24 template <class I>
25 inline void read(I &a, I &b) {read(a); read(b);}
26 template <class I>
27 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
28 
29 struct Node {
30     int id;
31     int rk;
32     int dis;
33     Node(int a, int b, int c) {id = a, rk = b, dis = c;}
34 
35     inline bool operator < (const Node& other) const {
36         return dis < other.dis;
37     }
38 };
39 
40 struct Trie {
41     int ch[2];
42     int size;
43 };
44 
45 int n, k;
46 ll ans;
47 int a[inf];
48 int sum[inf];
49 Trie t[inf * 32];
50 std::priority_queue <Node, std::vector <Node> > P;
51 
52 #define next  (t[now].ch[bit])
53 #define xnext (t[now].ch[bit ^ 1])
54 
55 inline void insert(int x) {
56     static int index = 0;
57     int now = 0;
58     for(int i = 31; i < 32; i--) {
59         int bit = (x >> i) & 1;
60         ++t[now].size;
61         if(next == 0) next = ++index;
62         now = next;
63     }
64     ++t[now].size;
65     return;
66 }
67 
68 inline int query(int x, int rk) {
69     int ans = 0;
70     int now = 0;
71     for(int i = 31; i < 32; i--) {
72         int bit = (x >> i) & 1;
73         if(xnext == 0) now = next;
74         else if(rk <= t[xnext].size) now = xnext, ans |= (1u << i);
75         else rk -= t[xnext].size, now = next;
76     }
77     return ans;
78 }
79 
80 inline int main(){
81     read(n, k); k <<= 1;
82     for(int i = 1; i <= n; i++) read(a[i]);
83     for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] ^ a[i];
84     for(int i = 0; i <= n; i++) insert(sum[i]);
85     for(int i = 0; i <= n; i++) P.push((Node){i, 1, query(sum[i], 1)});
86     for(int i = 1; i <= k; i++) {
87         Node x = P.top(); P.pop();
88         ans += x.dis;
89         if(x.rk >= n) continue;
90         P.push((Node){x.id, x.rk + 1, query(sum[x.id], x.rk + 1)});
91     }
92     printf("%lld\n", ans >> 1);
93     return 0;
94 }
95 
96 }
97 
98 signed main(){ return chiaro::main();}
亦或粽子

猜你喜欢

转载自www.cnblogs.com/Chiaro/p/problem-yihuozongzi.html