Memento Mori (二维前缀和 + 枚举剪枝)
Posted wethura
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Memento Mori (二维前缀和 + 枚举剪枝)相关的知识,希望对你有一定的参考价值。
枚举指的是枚举矩阵的上下界,然后根据p0, p1, p2的关系去找出另外的中间2个点。然后需要记忆化一些地方防止重复减少时间复杂度。这应该是最关键的一步优化时间,指的就是代码中to数组。然后就是子矩阵的一个计算了,需要用二维前缀和预处理数据,然后判断的时候直接O(1)查询就好了。
#include<bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 2e3 + 7; struct node{ int x, y; node(){} node(int x, int y) : x(x), y(y){} }a[maxn], b[maxn], c[maxn], X[4]; int p[4], matdp[maxn][maxn], to[maxn][2]; bool cmp1(const node &a, const node &b){ return a.x < b.x; } bool cmp2(const node &a, const node &b){ return a.y > b.y; } bool cmp3(const node &a, const node &b){ return a.y < b.y; } int howmany(int x1, int y1, int x2, int y2){ return matdp[x2][y2] + matdp[x1 - 1][y1 - 1] - matdp[x1 - 1][y2] - matdp[x2][y1 - 1]; } bool isOK(){ int x1 = inf, y1 = inf, x2 = 0, y2 = 0; for(int i = 0; i < 4; i ++){ x1 = min(x1, X[i].x); x2 = max(x2, X[i].x); y1 = min(y1, X[i].y); y2 = max(y2, X[i].y); for(int j = i + 1; j < 4; j ++) if((p[j] - p[i]) * (X[j].y - X[i].y) <= 0) return false; } return howmany(x1, y1, x2, y2) == 4; } int main(){ int T, n, m, k, x, y;scanf("%d",&T); while(T --){ scanf("%d%d%d", &n, &m, &k); scanf("%d%d%d%d", &p[0], &p[1], &p[2], &p[3]); int big = 0, small = 0, ans = 0; (p[0] > p[1]) ? small ++ : big ++; (p[0] > p[2]) ? small ++ : big ++; memset(matdp, 0, sizeof(matdp)); for(int i = 1; i <= k; i ++){ scanf("%d%d", &x, &y);matdp[x][y] = 1; a[i] = b[i] = c[i] = node(x, y); } for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++) matdp[i][j] += matdp[i][j - 1]; for(int j = 1; j <= m; j ++) matdp[i][j] += matdp[i - 1][j]; } sort(a + 1, a + k + 1, cmp1); sort(b + 1, b + k + 1, cmp2); sort(c + 1, c + k + 1, cmp3); for(int i = 1; i <= k; i ++){ X[0] = a[i]; for(int j = 0; j <= k; j ++) to[j][0] = to[j][1] = j == k ? 0 : j + 1; for(int j = k; j > i; j --){ if(a[i].x == a[j].x) break; if((p[3] - p[0]) * (a[j].y - a[i].y) <= 0) continue; X[3] = a[j]; X[1] = X[2] = node(-1, -1); int u = 0, v, bi = big, sm = small, t = 1; while(sm --){ for(v = u, u = to[u][0]; u; to[v][0] = to[u][0], v = u, u = to[u][0]) if(a[i].x < b[u].x && b[u].x < a[j].x && b[u].y < a[i].y){ X[t ++] = b[u];break; } } u = 0; while(bi --){ for(v = u, u = to[u][1]; u; to[v][1] = to[u][1], v = u, u = to[u][1]) if(a[i].x < c[u].x && c[u].x < a[j].x && c[u].y > a[i].y){ X[t ++] = c[u];break; } } if(t < 3 || X[1].x == X[2].x) continue; if(X[1].x > X[2].x) swap(X[1], X[2]); if(isOK()) ans ++; } } printf("%d ",ans); } return 0; }
以上是关于Memento Mori (二维前缀和 + 枚举剪枝)的主要内容,如果未能解决你的问题,请参考以下文章