上低音号

Description

\(r\times c\) 的矩阵里有 \(n\) 个点,询问至少包含 \(k\) 个点的子矩阵有多少个。 \(r,c,n\leq 3\times 10^3 , k\leq 10\)

Solution

容易想到一个 \(n^3\) 的做法。枚举上下边界,中间用 two-pointer 计算贡献。

但是没有用到 \(k\leq 10\) 这个性质。钦定一个点的贡献为使它成为矩阵最左边的点的合法矩阵个数,考虑对点进行增量更新,容易发现新增一个点只会对按横坐标排序后的往前数的 \(k\) 个点造成贡献。配合平衡树可以做到 \(O(n^2k\log n)\) 的复杂度。但这样不够优秀。瓶颈在于找下标,所以想到反过来操作,先排序后,用链表维护,然后只需要删除,这样就可以做到 \(O(n^2k)\)。注意边界细节。

#pragma GCC optimize(2)

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<cassert>
using namespace std;

typedef long long ll;

inline int read(){
    int x=0,flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

const int N=3e3+7;

struct Node{
    int x,y;
    bool operator <(const Node &X) const{
        return x>X.x;
    }
}a[N];

vector<int> V[N];
int head,tail,l[N],r[N];

inline void Ins(int x){
    l[x]=tail,r[tail]=x,tail=x;
}

int main(){
    freopen("baritone.in","r",stdin);
    freopen("baritone.out","w",stdout);
    int r_=read(),c_=read(),
        n=read(),k=read();
    for(int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read();
    sort(a+1,a+1+n);
    a[n+1]=(Node){0,0},a[0]=(Node){r_+1,0};
    ll ans=0;
    for(int L=1;L<=c_;L++){
        head=tail=0; int cnt=0;
        for(int i=1;i<=r_;i++) V[i].clear();
        for(int i=1;i<=n;i++)
            if(a[i].y>=L){
                Ins(i),cnt++;
                V[a[i].y].push_back(i);
            }
        if(cnt<k) break; Ins(n+1); ll ret=0;
        int p=head; for(int i=1;i<=k;i++) p=r[p];
        for(int i=r[head];p!=tail;i=r[i],p=r[p])
            ret+=1ll*(a[l[i]].x-a[i].x)*a[p].x;
        ans+=ret;
        for(int R=c_;R>L;R--){
            for(int j:V[R]){
                cnt--;
                int p=j; for(int i=1;i<k&&l[p]!=head;p=l[p],i++);
                int o=p; for(int i=1;i<k&&p!=tail;i++,p=r[p]);
                if(p==tail) continue;
                for(;p!=tail&&l[o]!=j;p=r[p],o=r[o])
                    ret-=1ll*(a[l[o]].x-a[o].x)*(a[p].x-a[r[p]].x);
            //    if(p!=tail) ret-=1ll*(a[l[o]].x-a[o].x)*a[p].x;
                r[l[j]]=r[j],l[r[j]]=l[j];
            }
            assert(ret>=0);
            if(cnt<k) break; ans+=ret;
        }
    }
    printf("%lld",ans);
}
上一篇:go数组实现环形队列


下一篇:力扣刷题-1