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 (二维前缀和 + 枚举剪枝)的主要内容,如果未能解决你的问题,请参考以下文章

(采油区域)二维前缀和+动态规划关系+分类讨论

激光炸弹(二维前缀和)

计蒜客模拟赛D1T1 蒜头君打地鼠:矩阵旋转+二维前缀和

每日一题31.「土」秘法地震 (二维前缀和 / DP)

Codeforces Round 871 (Div. 4) G. Hits Different (二维前缀和/思维)

二维差分前缀和——cf1202D(好题)