注意事项:
1. dfs序上线段树
2. 当是区间覆盖的时候,要记得判定pushdown能不能down,可能会造成错误的初始值覆盖
3. 在想维护哪些信息的时候,只需要考虑在已经这些清晰的情况下,如何递推计算这些信息以及维护标记就行了,不要绕进去
4. 结构体中有多个变量的时候,索引赋值更不容易出错(尤其是有数组的时候需要注意)
5. 扫描排序的时候,注意1类型在前还是-1类型在前
一个简单的整数问题
// 板子题目,维护区间加法的和
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
typedef long long LL;
// 标签在当前节点已经发生作用了,但是还没有对儿孙发生作用
int w[N];
struct node{
int l, r;
LL sum;
LL add;
}tr[4*N];
void pushup(int u){
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
// pusdown主要考虑标签融合,标签向下传递的计算两个方面
void pushdown(node &F, node &L, node &R){
// 标签融合
L.add += F.add;
R.add += F.add;
// 标签向下传递
L.sum = L.sum + (LL)(L.r - L.l + 1) * F.add;
R.sum = R.sum + (LL)(R.r - R.l + 1) * F.add;
// 标签清空
F.add = 0;
}
void pushdown(int u){
pushdown(tr[u], tr[u<<1], tr[u<<1|1]);
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, w[l], 0};
}
else{
tr[u] = {l, r, 0, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
}
// pushdown都是下面的分支里面才有,这样能保证pushdown不越界
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
// 这里修改的时候,标签要发挥作用
tr[u].add += v;
tr[u].sum += (tr[u].r - tr[u].l + 1) * v;
}
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
pushup(u);
}
}
LL query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else return query(u<<1, l, r) + query(u<<1|1, l, r);
}
}
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
build(1, 1, n);
while(m--){
char op[2]; int l, r, v;
scanf("%s", op);
if(op[0] == 'Q') scanf("%d %d", &l, &r), printf("%lld\n", query(1, l, r));
else scanf("%d %d %d", &l, &r, &v), modify(1, l, r, v);
}
return 0;
}
市长海报
题目:给定n个区间l-r,依次覆盖,问露出来的有多少个
存在的问题
1. 区间覆盖问题要离散的话时候可能会先问题,需要在离散化的时候加入l+1,r+1,这样才能正确区分
2. 该问题前面都是区间修改,后面都是单点查询,所以他就不需要pushup(),
直接在最后进行暴力的单点查询就可以了,而且也不需要维护区间的信息,只要维护好单点的信息就可以了
3. 如果是不需要离散化的区间覆盖呢,那么可以这么转化[l, l]代表[l, l+1]的颜色,那么覆盖[l, r],其实就是覆盖[l, r-1]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 4e4+10;
vector<int> pos;
PII seg[N];
// 标签在当前节点已经发生作用了,但是还没有对儿孙发生作用
int w[N];
struct node{
int l, r;
int type;
int label;
}tr[4*N];
void pushdown(node &F, node &L, node &R){
// 标签融合
if(F.label == 0) return ;
L.label = F.label;
R.label = F.label;
// 标签向下传递
L.type = F.label;
R.type = F.label;
// 标签清空
F.label = 0;
}
void pushdown(int u){
pushdown(tr[u], tr[u<<1], tr[u<<1|1]);
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, 0, 0};
}
else{
tr[u] = {l, r, 0, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
}
}
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].label = v;
tr[u].type = v;
}
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
}
}
int query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].type;
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else return query(u<<1, l, r) + query(u<<1|1, l, r);
}
}
int get(int x){
int l = 0, r = pos.size()-1;
while(l < r){
int mid = l + r + 1>> 1;
if(pos[mid] <= x) l = mid;
else r = mid - 1;
}
return l + 1;
}
int main(){
int T;
cin >> T;
while(T--){
int n; scanf("%d", &n);
pos.clear();
for(int i = 1; i <= n; i++){
int l, r; scanf("%d %d", &l, &r);
seg[i] = {l, r};
pos.push_back(l), pos.push_back(r);
pos.push_back(l+1), pos.push_back(r+1);
}
sort(pos.begin(), pos.end());
pos.erase(unique(pos.begin(), pos.end()), pos.end());
build(1, 1, pos.size());
for(int i = 1; i <= n; i ++){
int l = get(seg[i].first), r = get(seg[i].second);
modify(1, l, r, i);
}
// cout << 1 << endl;
set<int> S;
S.insert(0);
for(int i = 0; i < pos.size(); i++){
int x = get(pos[i]);
// cout << query(1, x, x ) << endl;
S.insert(query(1, x, x));
}
printf("%d\n", S.size()-1);
}
return 0;
}
你能回答这些问题吗
题目链接:https://www.acwing.com/problem/content/description/4347/
问题:
1.一道带暴力成分的线段树
2.n的和最多是1e6个,数据保证单组测试数据中ei的和不会超过2^63,发现2^63次方开个七八次根号就是1了,所以可以不打标记,
直接暴力修改到叶子,然后加一个剪枝就可以了,具体看代码
3. 时间复杂度,每个叶子最多被修改6,7次,按7次算的话,包含某个叶子权值的线段树节点,就是从这个叶子,不断地向上找父亲,
大概也就20个左右,这些就是将一个叶子的权值变成1最多要修改的次数7*20,最后再乘上叶子的总个数,也就n的和,是小于2e8
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
LL w[N];
struct node{
int l, r;
LL sum;
}tr[4*N];
void pushup(int u){
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, w[l]};
}
else{
tr[u] = {l, r, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
}
void modify(int u, int l, int r){
// 如果区间长度等于区间和的话,说明该区间都是1了,就不用递归开根号了
if(tr[u].r - tr[u].l + 1 == tr[u].sum) return ;
// 递归到叶子,直接对叶子修改
if(tr[u].l >= l && tr[u].r <= r && tr[u].l == tr[u].r) {
tr[u].sum = sqrt(tr[u].sum);
}
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r);
if(r > mid) modify(u<<1|1, l, r);
pushup(u);
}
}
LL query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else return query(u<<1, l, r) + query(u<<1|1, l, r);
}
}
int main(){
int C = 0, n;
while(cin >> n){
C++;
printf("Case #%d:\n", C);
for(int i = 1; i <= n; i++) scanf("%lld", &w[i]);
build(1, 1, n);
int m; scanf("%d", &m);
while(m --){
int op, l, r; scanf("%d %d %d", &op, &l, &r);
if(l > r) swap(l, r);
if(op == 0){
modify(1, l, r);
}
else if(op == 1){
printf("%lld\n", query(1, l, r));
}
}
puts("");
}
return 0;
}
地道战
问题:只有01两种元素的数组,动态单点修改,询问包含某个节点的最大连续1的长度
1.维护区间左端最大值,右端最大值,和整体的最大值,pushdown其实是比较好实现的
2.区间查询感觉比较困难,
为啥那么写查询是O(logn)的,而不会退化到O(n),每次递归的时候只会走一条分支,否则直接返回答案,所以
是log(n)(一共只有log层吧)
另一个就是跨区间的时候,可以直接返回答案,感觉比较难想
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 50010;
struct node{
int l, r;
int lmax, rmax, maxn;
}tr[4*N];
void pushup(int u){
node & F = tr[u], L = tr[u<<1], R = tr[u<<1|1];
F.maxn = max({L.maxn, R.maxn, L.rmax + R.lmax});
F.lmax = L.lmax;
F.rmax = R.rmax;
if(L.maxn == L.r - L.l + 1) F.lmax = max(F.lmax, L.maxn + R.lmax);
if(R.maxn == R.r - R.l + 1) F.rmax = max(F.rmax, R.maxn + L.rmax);
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, 1, 1, 1};
}
else{
tr[u] = {l, r, 1, 1, 1};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
}
void modify(int u, int x, int v){
if(tr[u].l == x && tr[u].r == x){
tr[u].lmax = tr[u].rmax = tr[u].maxn = v;
}
else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u<<1, x, v);
else modify(u<<1|1, x, v);
pushup(u);
}
}
int query(int u, int x){
if(tr[u].maxn == (tr[u].r - tr[u].l + 1) || tr[u].l == tr[u].r) return tr[u].maxn;
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid){
if(mid - tr[u<<1].rmax + 1<= x) return tr[u<<1].rmax + tr[u<<1|1].lmax;
else return query(u<<1, x);
}
else{
if(mid + 1 + tr[u<<1|1].lmax - 1 >= x) return tr[u<<1].rmax + tr[u<<1|1].lmax;
else return query(u<<1|1, x);
}
}
int main(){
int n, m;
while(cin >> n >> m){
build(1, 1, n);
stack<int> stk;
while(m--){
char op[2]; int x;
scanf("%s", op);
if(op[0] == 'D'){
scanf("%d", &x);
stk.push(x);
modify(1, x, 0);
}
else if(op[0] == 'Q'){
scanf("%d", &x);
printf("%d\n", query(1, x));
}
else{
if(stk.size() == 0) continue;
int top = stk.top(); stk.pop();
modify(1, top, 1);
}
}
}
return 0;
}
花瓶与鲜花
一道线段树上的二分,之前也写过一道线段树二分,但是这道和之前的感觉不太一样
之前那个直接query那里二分就可以了,这道题query部分还是正常的,又单独写了一个二分
另外一个关键点是,如何二分x之后的第一个和最后一个,以及如何判断是否不可能
首先可以query出来x之前有多少个空位,这样的话x及其之后第一个空位就是前面+1了,最后一个就算前面+y了
但是如何判断不可能呢,最后一个的计算,我们直接和tr[1]取个min这样的话,算出来的最多也就是最后一个
前面的边界的话,我们不管,这样如果有解的话,他就能算出来一个解,没有解的话他就自动二分道最后一个值了
例外需要注意的是二分里面不能继续缩短范围,不然的话,上面对l的计算可能就不正确了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4+10;
struct node{
int l, r;
int sum;
int add;
}tr[4*N];
void pushup(int u){
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void pushdown(node &F, node &L, node &R){
if(F.add == -1) return ;
// 标签融合
L.add = F.add;
R.add = F.add;
// 标签向下传递
L.sum = (L.r - L.l + 1) * F.add;
R.sum = (R.r - R.l + 1) * F.add;
// 标签清空
F.add = -1;
}
void pushdown(int u){
pushdown(tr[u], tr[u<<1], tr[u<<1|1]);
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, 1, -1};
}
else{
tr[u] = {l, r, r - l + 1, -1};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
}
}
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].add = v;
tr[u].sum = v * (tr[u].r - tr[u].l + 1);
}
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
pushup(u);
}
}
int query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else return query(u<<1, l, r) + query(u<<1|1, l, r);
}
}
int find(int u, int x){
if(tr[u].l == tr[u].r) return tr[u].l;
pushdown(u);
if(tr[u<<1].sum >= x ) return find(u<<1, x);
else return find(u<<1|1, x - tr[u<<1].sum);
}
int main(){
int T;
scanf("%d", &T);
while(T--){
int n, m; scanf("%d %d", &n, &m);
build(1, 0, n-1);
while(m--){
int op, x, y; scanf("%d %d %d", &op, &x, &y);
if(op == 1){
int lsum = 0;
if(x - 1 >= 0) lsum = query(1, 0, x - 1);
int L = find(1, lsum + 1);
int R = find(1, min(lsum + y, tr[1].sum));
modify(1, L, R, 0);
if( L > R ) printf("Can not put any one.\n");
else printf("%d %d\n", L, R);
}
else if(op == 2){
int pre = query(1, x, y);
modify(1, x, y, 1);
printf("%d\n", y - x + 1 - pre);
}
}
puts("");
}
return 0;
}
亚特兰蒂斯
问题:
1.扫描线板子题目,扫面线第一次扫描到的时候会区间+1,第二次扫到的时候会区间-1
2.方法,定义一个cnt表示该区间加的次数,定义一个长度
3. 不需要使用pushown操作,只会查询整个区间,所以下面的数值是不需要正确的
4. 注意pushup的操作,是需要区分叶子节点的,在修改的边界也是需要pushup的
5. 注意学习小数的离散化
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e4+10;
// 标签在当前节点已经发生作用了,但是还没有对儿孙发生作用
int w[N];
vector<double> pos;
struct Seg{
double x, l, r;
int type;
bool operator < (const Seg w) const{
return x < w.x;
}
}seg[N];
struct node{
int l, r;
int cnt;
double len;
}tr[4*N];
void pushup(int u){
if(tr[u].cnt >= 1){
tr[u].len = pos[tr[u].r ] - pos[tr[u].l - 1];
}
else if(tr[u].l != tr[u].r){
tr[u].len = tr[u<<1|1].len + tr[u<<1].len;
}
else{
tr[u].len = 0;
}
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, 0, 0};
}
else{
tr[u] = {l, r, 0, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
}
}
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].cnt += v;
pushup(u);
}
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
pushup(u);
}
}
double query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].len;
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else return query(u<<1, l, r) + query(u<<1|1, l, r);
}
}
int find(double x){
int l = 0, r = pos.size()-1;
while(l < r){
int mid = l + r + 1>> 1;
if(pos[mid] <= x) l = mid;
else r = mid - 1;
}
return l + 1;
}
int main(){
int n;
int T = 0;
while(cin >> n, n){
T ++;
int t = 0;
pos.clear();
for(int i = 1; i <= n; i++){
double x1, y1, x2, y2; scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
seg[t++] = {x1, y1, y2, 1};
seg[t++] = {x2, y1, y2, -1};
pos.push_back(y1), pos.push_back(y2);
}
sort(seg, seg+t);
sort(pos.begin(), pos.end());
// for(auto c : pos) cout << c << endl;
pos.erase(unique(pos.begin(), pos.end()), pos.end());
build(1, 1, pos.size() + 1);
double res = 0;
modify(1, find(seg[0].l), find(seg[0].r) - 1, 1);
for(int i = 1; i < t; i++){
// cout << (seg[i].x - seg[i-1].x) << ' ' << query(1, 1, pos.size()) << endl;
res += (seg[i].x - seg[i-1].x) * query(1, 1, pos.size());
modify(1, find(seg[i].l), find(seg[i].r) - 1, seg[i].type);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n", T, res);
}
return 0;
}
覆盖的面积
重复覆盖两次的面积
1.需要记录覆盖一次的长度和覆盖两次的长度
2.只记录覆盖两次的长度的话,比如某个大区间只被覆盖了一次,但是他的一个小区间也被覆盖了一次
这样会造成返回0,
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 20010;
vector<double> pos;
struct Seg{
double x, l, r, type;
bool operator < (const Seg & w) const{
return x < w.x;
}
}seg[N];
struct node{
int l, r, cnt;
double len1, len2;
}tr[4*N];
void pushup(int u){
if(tr[u].cnt) tr[u].len1 = pos[tr[u].r] - pos[tr[u].l-1];
else if(tr[u].l != tr[u].r) tr[u].len1 = tr[u<<1].len1 + tr[u<<1|1].len1;
else tr[u].len1 = 0;
if(tr[u].cnt >= 2) tr[u].len2 = pos[tr[u].r] - pos[tr[u].l-1];
else if(tr[u].cnt == 1) tr[u].len2 = tr[u<<1].len1 + tr[u<<1|1].len1;
else if(tr[u].l != tr[u].r) tr[u].len2 = tr[u<<1].len2 + tr[u<<1|1].len2;
else tr[u].len2 = 0;
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, 0, 0};
}
else{
tr[u] = {l, r, 0, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
}
}
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].cnt += v;
pushup(u);
}
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
pushup(u);
}
}
int find(double x){
int l = 0, r = pos.size()-1;
while(l < r){
int mid = l + r + 1>> 1;
if(pos[mid] <= x) l = mid;
else r = mid - 1;
}
return l + 1;
}
int main(){
int T; cin >> T;
while(T--){
int n; scanf("%d", &n);
pos.clear();
int t = 0;
while(n--){
double a, b, c, d;
scanf("%lf %lf %lf %lf", &a, &b, &c, &d);
seg[t++] = {b, a, c, 1};
seg[t++] = {d, a, c, -1};
pos.push_back(a), pos.push_back(c);
}
sort(pos.begin(), pos.end());
pos.erase(unique(pos.begin(), pos.end()), pos.end());
double res = 0;
sort(seg, seg+t);
build(1, 1, pos.size());
for(int i = 0; i < t; i++){
if(i > 0) res += (seg[i].x - seg[i-1].x) * tr[1].len2;
modify(1, find(seg[i].l), find(seg[i].r) - 1, seg[i].type);
}
// cout << double << endl;
printf("%.2lf\n", res);
}
return 0;
}
获取宝藏
1. 注意附件多次覆盖的求解方法,pushup部分
2. 遍历z的时候如何计算当前切面的覆盖面积->只要包括当前切面的长方体就可以
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
const int N = 3010;
int posx[N], posy[N], posz[N];
int xcnt = 0, ycnt = 0, zcnt = 0;
struct Plane{
int x, y1, y2, z1, z2, type;
bool operator < (const Plane & w) const{
return x < w.x;
}
}plane[N], tmp[N];
int w[N];
struct node{
int l, r;
int len1, len2, len3, cnt;
}tr[4*N];
void pushup(int u){
if(tr[u].cnt >= 1) tr[u].len1 = posy[tr[u].r] - posy[tr[u].l - 1];
else if(tr[u].l != tr[u].r) tr[u].len1 = tr[u<<1].len1 + tr[u<<1|1].len1;
else tr[u].len1 = 0;
if(tr[u].cnt >= 2) tr[u].len2 = posy[tr[u].r] - posy[tr[u].l - 1];
else if(tr[u].cnt == 1) tr[u].len2 = tr[u<<1].len1 + tr[u<<1|1].len1;
else if(tr[u].l != tr[u].r) tr[u].len2 = tr[u<<1].len2 + tr[u<<1|1].len2;
else tr[u].len2 = 0;
if(tr[u].cnt >= 3) tr[u].len3 = posy[tr[u].r] - posy[tr[u].l - 1];
else if(tr[u].cnt == 2) tr[u].len3 = tr[u<<1].len1 + tr[u<<1|1].len1;
else if(tr[u].cnt == 1) tr[u].len3 = tr[u<<1].len2 + tr[u<<1|1].len2;
else if(tr[u].l != tr[u].r) tr[u].len3 = tr[u<<1].len3 + tr[u<<1|1].len3;
else tr[u].len3 = 0;
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, 0, 0, 0, 0};
}
else{
tr[u] = {l, r, 0, 0, 0, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
}
}
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].cnt += v;
pushup(u);
}
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
pushup(u);
}
}
int find(int x){
int l = 0, r = ycnt - 1;
while(l < r){
int mid = l + r + 1>> 1;
if(posy[mid] <= x) l = mid;
else r = mid - 1;
}
return l + 1;
}
int main(){
int T; scanf("%d", &T);
for(int C = 1; C <= T; C ++ ){
int n; scanf("%d", &n);
int t = 0;
xcnt = 0, ycnt = 0, zcnt = 0;
for(int i = 1; i <= n; i++){
int x1, y1, x2, y2, z1, z2; scanf("%d %d %d %d %d %d", &x1, &y1, &z1, &x2, &y2, &z2);
plane[t ++] = {x1, y1, y2, z1, z2, 1};
plane[t ++] = {x2, y1, y2, z1, z2, -1};
posx[xcnt++] = x1, posx[xcnt++] = x2;
posy[ycnt++] = y1, posy[ycnt++] = y2;
posz[zcnt++] = z1, posz[zcnt++] = z2;
}
sort(posx, posx + xcnt); xcnt = unique(posx, posx + xcnt) - posx;
sort(posy, posy + ycnt); ycnt = unique(posy, posy + ycnt) - posy;
sort(posz, posz + zcnt); zcnt = unique(posz, posz + zcnt) - posz;
sort(plane, plane + t);
// cout << xcnt << ' ' << ycnt << ' ' << zcnt << endl << endl;
LL res = 0;
for(int zz = 0; zz < zcnt; zz ++){
int znow = posz[zz];
int cnt = 0;
for(int i = 0; i < t; i++){
if(plane[i].z1 <= znow && plane[i].z2 > znow) tmp[cnt ++] =plane[i];
}
sort(tmp, tmp+cnt);
build(1, 1, ycnt);
LL area = 0;
for(int i = 0; i < cnt; i++){
if(i) area += ((LL)tmp[i].x - tmp[i-1].x) * tr[1].len3;
modify(1, find(tmp[i].y1), find(tmp[i].y2) - 1, tmp[i].type);
// cout << tmp[i].x << ' ' << tmp[i-1].x << ' ' << tr[1].len3 << endl;
}
res += ((LL)posz[zz+1] - posz[zz]) * area;
}
printf("Case %d: %lld\n", C, res);
}
return 0;
}