当前位置: 首页 > 工具软件 > Baritone > 使用案例 >

baritone 上低音号(链表 卡矩阵)

单于扬
2023-12-01

baritone

上低音号
【题目描述】
黄前久美子是一名上低音号手。这天,一共有n名上低音号手参加演出。演出的场地是一个r行c列的矩阵,每名上低音号手都在其中一个方格内,且没有两个人在同一个方格。
摄影师为这次演出拍摄了许许多多的照片,每张照片都是一个边框平行于坐标轴的矩形。久美子希望照片中能出现尽量多的上低音号手——具体地,如果一张照片出现了超过k名上低音号手,那么久美子会很喜欢这张照片。
那么,究竟能拍出多少张不同的久美子喜爱的照片呢?
【输入格式】
第一行四个整数r,c,n,k。
接下来n行,每行两个数x,y,表示一名在第x行第y列的上低音号手。保证数对(x,y)不重复出现。
【输出格式】
输出一行一个整数,表示可以拍出的久美子喜爱的照片数量。
【样例数据】
baritone1.in
2 2 1 1
1 2
baritone1.out
4
baritone2.in
3 2 3 3
1 1
3 1
2 2
baritone2.out
1
baritone3.in
3 2 3 2
1 1
3 1
2 2
baritone3.out
4
baritone4.in
1 1 1 1
1 1
baritone4.out
1
【数据范围】
对于30%的数据,n,r,c≤500。
对于另30%的数据,k≤3。
对于100%的数据,1≤n,r,c≤3000,1≤k≤10。

思路:
从上往下枚举上边界,从下往上枚举下边界。
考虑已经统计出上下边界为枚举的包含至少k 个点的矩形数,当下边界往上移动时,可能会删除某些点。
用按横坐标排序的有序表维护当前上下边界间的点,只有删除点的前k 个点和后k个点会影响答案,其它矩形区域依然可行。
这样每次只需要从有序表中删除一个点并统计答案,使用链表即可。
复杂度O( n^2*k )。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <queue>
#include <cmath>
using namespace std;

const int N = 4000;
int r, c, n, k, idc;
int nxt[N], pre[N], val[N], id[N];
int pointx[N], finded[N], pointy[N];
long long ans, sum;
vector <int> ve[N];

struct AA{
    int x, y;
}aa[N];

bool cmp(int x, int y) {
    if (pointx[x] != pointx[y]) return pointx[x] < pointx[y];
    else return pointy[x] < pointy[y];
}

void del(int t) {
    nxt[pre[t]] = nxt[t];
    pre[nxt[t]] = pre[t];//从链表中删除 
    sum -= val[t];//已t.x为左边界的矩形要减去 
    int x = nxt[t], y = nxt[t];
    for (int i=1; i<k; i++) y = nxt[y];//找到恰好在t之后的k个点 
    for (int i=1; i<=k; i++) {//一直到刚好包含t的k个点(都会被影响) 
        int v = (pointx[x] - pointx[pre[x]]) * (r - pointx[y] + 1);
        sum += v - val[x];
        val[x] = v;
        x = pre[x]; y = pre[y];
    }
}

int main() {
    //freopen("baritone.in", "r", stdin);
    //freopen("baritone.out", "w", stdout);
    scanf("%d%d%d%d", &r, &c, &n, &k);
    ans = 0;
    for (int i=1; i<=n; i++)
        scanf("%d%d", &aa[i].x, &aa[i].y), ve[aa[i].y].push_back(i);//保存每一列有那些点 
    for (int i=1; i<=c; i++) {//矩形上边界为i 
        sum = 0; idc = 0;
        for (int j=0; j<=10; j++)
            pointx[++idc] = 0, pointx[++idc] = r + 1;//防止跳k-1步越界 
        for (int j=1; j<=n; j++)//可能在当前矩阵里的点 
            if (aa[j].y >= i) {
                pointx[++idc] = aa[j].x;
                pointy[idc] = aa[j].y;
                finded[j] = idc;
            }
        for (int j=1; j<=idc; j++) id[j] = j;
        sort(id+1, id+idc+1, cmp);//x为第一关键字,y为第二关键字排序 
        for (int j=1; j<idc; j++)//
            nxt[id[j]] = id[j+1], pre[id[j+1]] = id[j];
        nxt[id[idc]] = id[idc];
        pre[id[1]] = id[1];//建立链表维护前驱后继 
        for (int j=1; j<=idc; j++) {
            int now = j;//枚举矩形左边界j.x 
            for (int p=1; p<k; p++) now = nxt[now];//now为矩形右边界 
            val[j] = (pointx[j] - pointx[pre[j]]) * (r - pointx[now] + 1);//左边界在j与pre[j]之间,右边界在now与r之间都满足条件 
            sum += val[j];//val[j]表示在当前上下界是,左边界在j.x且满足条件的矩形有多少个 
        }
        for (int j=c; j>=i; j--) {//枚举下边界 
            ans += sum;
            for (int p=0; p<(int)ve[j].size(); p++)//原来下边界上的所有点都要被delete 
                del(finded[ve[j][p]]);//finded[i]是第i个点在链表中的序号 
        }
    }
    cout << ans << endl;
    return 0;
}
 类似资料: