cdq分治
又被称为基于时间的离线分治算法,它能够解决问题的关键之处在于能够对操作序列按照“时间”分治,让转化后的静态问题不必考虑“修改”和“查询”的时间顺序,使按照其他信息(例如横坐标)排序成为可能,降低了数据结构需要控制的限制条件的维度。大多数cdq分治的题目都可以转化成三维偏序问题,和树状数组配合应用,不过也有一些例外,比如“所有区间问题"只用到了cdq分治合并时以中点为界计算区间对区间相互影响的巧妙思想。
F. 陌上花开
保存一下模板——
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; int n, k, mx, top, m, c[maxn], su[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int a, b, c, cnt, ans; }s1[maxn], s2[maxn]; bool cmp1(node x, node y) { if(x.a == y.a) { if(x.b == y.b) return x.c < y.c; else return x.b < y.b; } else return x.a < y.a; } bool cmp2(node x, node y) { if(x.b == y.b) { return x.c < y.c; } else return x.b < y.b; } int lowbit(int x) { return x & -x; } void add(int x, int y) { while(x <= mx) { c[x] += y; x += lowbit(x); } } int query(int x) { int sum = 0; while(x) { sum += c[x]; x -= lowbit(x); } return sum; } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); cdq(mid+1, r); sort(s2+l, s2+mid+1, cmp2); sort(s2+mid+1, s2+r+1, cmp2); int i, j=l; for(i=mid+1; i<=r; i++) { while(s2[i].b >= s2[j].b && j<=mid) { add(s2[j].c, s2[j].cnt); j++; } s2[i].ans += query(s2[i].c); } for(i=l; i<j; i++) { add(s2[i].c, -s2[i].cnt); } } int main() { n = read(); k = read(); mx = k; for(int i=1; i<=n; i++) { s1[i].a = read(); s1[i].b = read(); s1[i].c = read(); } sort(s1+1, s1+1+n, cmp1); for(int i=1; i<=n; i++) { top++; if(s1[i].a!=s1[i+1].a || s1[i].b!=s1[i+1].b || s1[i].c!=s1[i+1].c) { m++; s2[m].a = s1[i].a; s2[m].b = s1[i].b; s2[m].c = s1[i].c; s2[m].cnt = top; top = 0; } } cdq(1, m); for(int i=1; i<=m; i++) { su[s2[i].ans+s2[i].cnt-1] += s2[i].cnt; } for(int i=0; i<n; i++) { printf("%d\n", su[i]); } return 0; }
G. Mokia && I. 简单题
MineFirst:由于修改是否在我要查询的矩形的范围内不能依靠两组数据的比较判断出来,也没有办法找到一种排序方式使j不用回跳,我表示真的很没思路,但还是牵强的写了一个伪cdq,hz题库上TLE 20,不过交到洛谷上TLE 50。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 10; int s, w, m; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int x, y, u, r, op, ans; }q[maxn]; void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); cdq(mid+1, r); for(int i=mid+1; i<=r; i++) { if(q[i].op == 2) { for(int j=l; j<=mid; j++) { if(q[j].op == 2) continue; //printf("111\n"); //printf("cmp %d %d %d %d %d %d\n", q[j].x, q[i].x, q[j].x, q[i].u, q[j].y, q[i].y); if(q[j].x>=q[i].x && q[j].x<=q[i].u && q[j].y>=q[i].y && q[j].y<=q[i].r) { q[i].ans += q[j].u; //printf("q[%d].ans += %d\n", i, q[j].u); } } } } } int main() { s = read(); w = read(); while(1) { int op = read(); if(op == 3) break; q[++m].op = op; if(op == 1) { q[m].x = read(); q[m].y = read(); q[m].u = read(); } else { q[m].x = read(); q[m].y = read(); q[m].u = read(); q[m].r = read(); } } cdq(1, m); for(int i=1; i<=m; i++) { if(q[i].op == 2) { //printf("q[%d].ans = %d\n", i, q[i].ans); printf("%d\n", q[i].ans+s*(q[i].x-q[i].u+1)*(q[i].r-q[i].u+1)); } } return 0; }
正解解决以上问题的方法是把问题拆分,全都变成前缀的形式,只比较“上界”,这样时间,x,y,就构成了三维偏序,拆出来的矩形有的做正贡献有的做负贡献,就是二维树状数组用的套路了。需要注意拆分问题导致空间要求更大,开数组之前好好想想!!!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 10; const int N = 2e6 + 10; int s, w, cnt, c[N]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int tim, x, y, val, id; }e[maxn]; bool cmp(node a, node b) { return a.tim < b.tim; } bool cmp1(node a, node b) { if(a.x == b.x) { return a.y < b.y; } else return a.x < b.x; } inline int lowbit(int x) { return x & -x; } void add(int x, int y) { while(x <= w) { c[x] += y; x += lowbit(x); } } int query(int x) { int sum = 0; while(x) { sum += c[x]; x -= lowbit(x); } return sum; } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); cdq(mid+1, r); sort(e+l, e+mid+1, cmp1); sort(e+mid+1, e+r+1, cmp1); int i=l, j = mid+1; for(; j<=r; j++) { while(e[i].x <= e[j].x && i<=mid) { if(e[i].id == 0) add(e[i].y, e[i].val); i++; } if(e[j].id == 1) e[j].val += query(e[j].y); } for(j=l; j<i; j++) { if(e[j].id == 0) add(e[j].y, -e[j].val); } } int main() { s = read(); w = read()+1; int opt = read(); while(opt != 3) { if(opt == 1) { int x = read()+1, y = read()+1, val = read(); e[++cnt] = (node){cnt, x, y, val, 0}; } else { int x1 = read(), y1 = read(), x2 = read()+1, y2 = read()+1; e[++cnt] = (node){cnt, x1, y1, 0, 1}; e[cnt].val += s*(x2-x1)*(y2-y1); e[++cnt] = (node){cnt, x2, y2, 0, 1}; e[++cnt] = (node){cnt, x2, y1, 0, 1}; e[++cnt] = (node){cnt, x1, y2, 0, 1}; } opt = read(); } cdq(1, cnt); sort(e+1, e+cnt+1, cmp); for(int i=1; i<=cnt; i++) { if(e[i].id == 1) { printf("%d\n", e[i].val+e[i+1].val-e[i+2].val-e[i+3].val); i += 3; } } return 0; }
“简单题”的原版好像是强制在线的,被hz题库改简单了。
H. 天使玩偶
看完书上的讲解我还以为需要开4个树状数组,一遍cdq分出4种情况来讨论,这4个树状数组还要附上-INF的初值……然后我终于发现把曼哈顿距离化简后的4个式子都是前后一致的,代表加上啥就能消掉啥,所以cdq时的格式不变。还学到了一种奇妙的处理,复制数组tem能起到排序的效果,省下了sort的时间。
还有就是,清空树状数组时不要判断while(c[x]>0),这样就又RE了,直接清空到头就好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e6 + 10; const int N = 2e6 + 10; const int M = 1e7 + 10; const int INF = 2e7 + 10; int n, q, opt, x, y, len, c[M]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int x, y, type, id, ans; }a[maxn], b[maxn], tem[maxn]; inline int lowbit(int x) { return x & -x; } void update(int x, int y) { while(x <= len) { c[x] = max(c[x], y); x += lowbit(x); } } int query(int x) { int ans = 0; while(x) { ans = max(ans, c[x]); x -= lowbit(x); } return ans ? ans : -INF; } void clean(int x) { while(c[x]) { c[x] = 0; x += lowbit(x); } } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); cdq(mid+1, r); int j = l, k = l; for(int i=mid+1; i<=r; i++) { while(j<=mid && b[j].x<=b[i].x) { if(b[j].type == 1) { update(b[j].y, b[j].x+b[j].y); } tem[k] = b[j]; k++; j++; } if(b[i].type == 2) { a[b[i].id].ans = min(a[b[i].id].ans, b[i].x+b[i].y-query(b[i].y)); } tem[k] = b[i]; k++; } for(int i=l; i<j; i++) { if(b[i].type == 1) { clean(b[i].y); } } while(j<=mid) { tem[k] = b[j]; k++; j++; } for(int i=l; i<=r; i++) b[i] = tem[i]; } void solve(int rx, int ry) { for(int i=1; i<=n+q; i++) { b[i] = a[i]; if(rx) b[i].x = len - b[i].x; if(ry) b[i].y = len - b[i].y; } cdq(1, n+q); } int main() { n = read(); q = read(); for(int i=1; i<=n; i++) { x = read(); y = read(); a[i].type = 1; a[i].id = i; a[i].x = ++x; a[i].y = ++y; len = max(len, max(x, y)); } for(int i=n+1; i<=n+q; i++) { opt = read(); x = read(); y = read(); a[i].type = opt; a[i].id = i; a[i].x = ++x; a[i].y = ++y; a[i].ans = INF; len = max(len, max(x, y)); } len++; solve(0, 0); solve(0, 1); solve(1, 0); solve(1, 1); for(int i=n+1; i<=n+q; i++) { if(a[i].type == 2) { printf("%d\n", a[i].ans); } } return 0; }
J. 动态逆序对
会暴力就先写暴力——
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; int n, m, a[maxn], pos[maxn], ans, c[maxn]; bool v[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } inline int lowbit(int x) { return x & -x; } void add(int x, int y) { while(x <= n) { c[x] += y; x += lowbit(x); } } int query(int x) { int ans = 0; while(x) { ans += c[x]; x -= lowbit(x); } return ans; } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { a[i] = read(); pos[a[i]] = i; } while(m--) { ans = 0; for(int i=1; i<=n; i++) { if(!v[i]) { ans += query(n) - query(a[i]); //printf("query(%d)\n", a[i]); add(a[i], 1); //printf("add(%d, 1)\n", a[i]); } } printf("%d\n", ans); for(int i=1; i<=n; i++) { if(!v[i]) { add(a[i], -1); //printf("add(%d, -1)\n", a[i]); } } int x = read(); v[pos[x]] = 1; } return 0; }
还是三维偏序——消失的逆序对=位置在它前面,权值比它大,删去时间比它晚+位置在它后面,权值比它大,删去时间比它晚。添加和删去一个做正贡献一个做负贡献,它的贡献方向无论是更新时还是计算答案时都是一致的,还要注意为了使j具有“可传递”的性质优化时间,循环可以倒着写,反套路*1,但下面那个题才是真正的反套路。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; int n, m, tot, pos[maxn], a[maxn], c[maxn]; ll ans[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int m, v, d, id, t; }e[maxn<<1]; bool cmp(node x, node y) { return x.d < y.d; } inline int lowbit(int x) { return x & -x; } void add(int x, int y) { while(x <= n) { c[x] += y; x += lowbit(x); } } int query(int x) { int ans = 0; while(x) { ans += c[x]; x -= lowbit(x); } return ans; } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); cdq(mid+1, r); sort(e+l, e+mid+1, cmp); sort(e+mid+1, e+r+1, cmp); int j = l; for(int i=mid+1; i<=r; i++) { while(j<=mid && e[j].d<=e[i].d) { add(e[j].v, e[j].m); j++; } ans[e[i].id] += e[i].m*(query(n)-query(e[i].v)); } for(int i=l; i<j; i++) { add(e[i].v, -e[i].m); } j = mid; for(int i=r; i>mid; i--) { while(j>=l && e[j].d>=e[i].d) { add(e[j].v, e[j].m); j--; } ans[e[i].id] += e[i].m*query(e[i].v-1); } for(int i=mid; i>j; i--) { add(e[i].v, -e[i].m); } } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { a[i] = read(); pos[a[i]] = i; e[++tot] = {1, a[i], i, 0, tot}; } for(int i=1; i<=m; i++) { int x = read(); e[++tot] = {-1, x, pos[x], i, tot}; } cdq(1, tot); for(int i=1; i<=m; i++) { ans[i] += ans[i-1]; } for(int i=0; i<m; i++) { printf("%lld\n", ans[i]); } return 0; }
K. 序列
第一印象:这不是个dp吗?和cdq有什么关系?!然后写了个暴力交上去WA 10分,我才发现我只把f[1]预处理成了1,其实所有的f都应该被预处理成1,改完这个之后就是TLE 50,天哪我要是赶上这场省选现跑40分……
我惊奇的发现左右两个区间的排序标准可以不同,这道题居然也转化为三维偏序了,我还发现cdq(l, mid)和cdq(mid+1, r)不一定连着,刷题就是一个学会套路再推翻套路的过程,改变顺序之后一定要在cdq(mid+1, r)之前按照时间排好序,就像清空树状数组一样,仔细想的话既觉得很合理又觉得不太理解的操作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; int n, m, f[maxn], ans; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int num, max, min; }p[maxn]; int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { p[i].num = read(); p[i].max = p[i].min = p[i].num; } for(int i=1; i<=m; i++) { int x = read(), y = read(); p[x].max = max(p[x].max, y); p[x].min = min(p[x].min, y); } for(int i=1; i<=n; i++) f[i] = 1; for(int i=2; i<=n; i++) { for(int j=1; j<i; j++) { if(p[i].num>=p[j].max && p[i].min>=p[j].num) { f[i] = max(f[i], f[j]+1); } } } for(int i=1; i<=n; i++) { ans = max(ans, f[i]); } printf("%d\n", ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const int maxc = 1e5; int n, m, f[maxn]; int ans, c[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int num, max, min, id; }p[maxn]; bool cmp1(node x, node y) { return x.max < y.max; } bool cmp2(node x, node y) { return x.num < y.num; } bool cmp3(node x, node y) { return x.id < y.id; } inline int lowbit(int x) { return x & -x; } void update(int x, int y) { while(x <= maxc) { c[x] = max(c[x], y); x += lowbit(x); } } int query(int x) { int ans = 0; while(x) { ans = max(ans, c[x]); x -= lowbit(x); } return ans; } void clean(int x) { while(x <= maxc) { c[x] = 0; x += lowbit(x); } } void cdq(int l, int r) { if(l == r) { f[l] = max(f[l], 1); return; } int mid = (l + r) >> 1; cdq(l, mid); sort(p+l, p+mid+1, cmp1); sort(p+mid+1, p+r+1, cmp2); int j = l; for(int i=mid+1; i<=r; i++) { while(j<=mid && p[j].max<=p[i].num) { update(p[j].num, f[p[j].id]); j++; } f[p[i].id] = max(f[p[i].id], query(p[i].min)+1); } for(int i=l; i<j; i++) { clean(p[i].num); } sort(p+l, p+r+1, cmp3); cdq(mid+1, r); } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { p[i].num = read(); p[i].max = p[i].min = p[i].num; p[i].id = i; } for(int i=1; i<=m; i++) { int x = read(), y = read(); p[x].max = max(p[x].max, y); p[x].min = min(p[x].min, y); } cdq(1, n); for(int i=1; i<=n; i++) { ans = max(ans, f[i]); } printf("%d\n", ans); return 0; }
三维偏序时按照谁先排序其实都可以,所以还有另一种写法,上面那几个题应该都有另一种写法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const int maxc = 1e5; int n, m; int ans, c[maxn], f[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } struct node { int num, max, min, id; }p[maxn]; bool cmp1(node x, node y) { return x.num < y.num; } bool cmp2(node x, node y) { return x.min < y.min; } bool cmp3(node x, node y) { return x.id < y.id; } inline int lowbit(int x) { return x & -x; } void update(int x, int y) { while(x <= maxc) { c[x] = max(c[x], y); x += lowbit(x); } } int query(int x) { int ans = 0; while(x) { ans = max(ans, c[x]); x -= lowbit(x); } return ans; } void clean(int x) { while(x <= maxc) { c[x] = 0; x += lowbit(x); } } void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); sort(p+l, p+mid+1, cmp1); sort(p+mid+1, p+r+1, cmp2); int j=l; for(int i=mid+1; i<=r; i++) { while(j<=mid && p[j].num<=p[i].min) { update(p[j].max, f[p[j].id]); j++; } f[p[i].id] = max(f[p[i].id], query(p[i].num)+1); } for(int i=l; i<j; i++) { clean(p[i].max); } sort(p+l, p+r+1, cmp3); cdq(mid+1, r); } /* void cdq(int l, int r) { if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); sort(p+l, p+mid+1, cmp1); sort(p+mid+1, p+r+1, cmp2); int i=l, j=mid+1; for(; j<=r; j++) { while(i<=mid && p[i].num<) } } */ int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { p[i].num = read(); p[i].max = p[i].min = p[i].num; p[i].id = i; } for(int i=1; i<=m; i++) { int x = read(), y = read(); p[x].max = max(p[x].max, y); p[x].min = min(p[x].min, y); } for(int i=1; i<=n; i++) f[i] = 1; cdq(1, n); for(int i=1; i<=n; i++) { ans = max(ans, f[i]); } printf("%d\n", ans); return 0; }
也有一篇题解没有用结构体!感觉挺神奇,但也没有理解的很好,我还是更喜欢结构体一点吧。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const int maxc = 1e5; int n, m, a[maxn], mx[maxn], mn[maxn], f[maxn]; int p[maxn], ans, c[maxn]; inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch > '9' || ch < '0') { if(ch == '-') { f = -1; } ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch^48); ch = getchar(); } return x * f; } bool cmp1(int i, int j) { return mx[i] < mx[j]; } bool cmp2(int i, int j) { return a[i] < a[j]; } inline int lowbit(int x) { return x & -x; } void update(int x, int y) { while(x <= maxc) { c[x] = max(c[x], y); x += lowbit(x); } } int query(int x) { int ans = 0; while(x) { ans = max(ans, c[x]); x -= lowbit(x); } return ans; } void clean(int x) { while(x <= maxc) { c[x] = 0; x += lowbit(x); } } void cdq(int l, int r) { if(l == r) { f[l] = max(f[l], 1); return; } int mid = (l + r) >> 1; cdq(l, mid); for(int i=l; i<=r; i++) { p[i] = i;//在排序之前保存原始编号 } sort(p+l, p+mid+1, cmp1); sort(p+mid+1, p+r+1, cmp2); int j = l; for(int i=mid+1; i<=r; i++) { while(j<=mid && mx[p[j]]<=a[p[i]]) { update(a[p[j]], f[p[j]]); j++; } f[p[i]] = max(f[p[i]], query(mn[p[i]])+1); } for(int i=l; i<=mid; i++)//为什么没有循环到<j { clean(a[i]); } cdq(mid+1, r);//这是什么顺序?不按套路啊,不过确实只有前半段有用 //所以还是相当于从左到右循环?优化的是找到最优解的过程 } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) { a[i] = read(); mx[i] = mn[i] = a[i]; } for(int i=1; i<=m; i++) { int x = read(), y = read(); mx[x] = max(mx[x], y); mn[x] = min(mn[x], y); } cdq(1, n); for(int i=1; i<=n; i++) { ans = max(ans, f[i]); } printf("%d\n", ans); return 0; }
“追梦的人,要有内卷的觉悟”——Cat的假期生活。然后你就发现了Cat的RE日常和Cat的抄题解日常……惭愧地说。
A. inversions
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = (1<<20) + 5; int a[maxn], q[maxn], n, m, threshold, cnt, ls[maxn], t[maxn]; ll rm[23], r1[23], las; bool now[23]; ull k1, k2; ull xorShift128Plus() { ull k3 = k1, k4 = k2; k1 = k4; k3 ^= (k3 << 23); k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26); return k2 + k4; } void gen(int n, int m, int threshold, ull _k1, ull _k2) { k1 = _k1, k2 =_k2; for (int i=1; i<=(1<<n); i++) a[i] = xorShift128Plus() % threshold + 1; for (int i=1; i<=m; i++) q[i] = xorShift128Plus() % (n + 1); } //用归并排序预处理每个长度对应的逆序对数,区间长度是2^n,分治一下那不就正好是……!!! void solve(int l, int r, int dep, bool op) { if(l >= r) return; int mid = (l + r) >> 1; solve(l, mid, dep-1, op); solve(mid+1, r, dep-1, op); int p1 = l, pos = l-1; for(int p2=mid+1; p2<=r; p2++) { while(p1<=mid && ls[p1]<=ls[p2]) { t[++pos] = ls[p1]; p1++; } if(op) r1[dep] += mid - p1 + 1; else rm[dep] += mid - p1 + 1; t[++pos] = ls[p2]; } while(p1 <= mid) t[++pos] = ls[p1], p1++; for(int i=l; i<=r; i++) ls[i] = t[i]; } int main() { scanf("%d%d%d%lld%lld", &n, &m, &threshold, &k1, &k2); gen(n, m, threshold, k1, k2); if(n == 0) { printf("0\n"); exit(0); } int mx = (1<<n); for(int i=1; i<=mx; i++) ls[i] = a[i]; sort(ls+1, ls+1+mx); cnt = unique(ls+1, ls+1+mx)-ls-1; for(int i=1; i<=mx; i++) { a[i] = lower_bound(ls+1, ls+cnt+1, a[i])-ls; } for(int i=1; i<=mx; i++) ls[i] = a[i]; solve(1, mx, n, 1); for(int i=1; i<=mx; i++) ls[i] = a[mx-i+1]; solve(1, mx, n, 0); ll ans = 0; for(int i=1; i<=m; i++) { ll las = 0; now[q[i]] = 1 - now[q[i]]; int fl = 1; for(int j=n; j>=1; j--) { if(now[j]) fl = 1-fl; if(!fl) las += rm[j]; else las += r1[j]; } ans = ans ^ (las * i); } printf("%lld\n", ans); return 0; }