【学习笔记】CF627E Orchestra

感觉最近似乎做不出什么题。

耐人寻味的数据范围。熟悉的问题。但是要 结合数据范围 自己编一个做法出来还挺难的。

我太菜了啊

我尝试去感知这道题目。对于二维坐标的限制无疑是困难的,不妨考虑当固定上下边界时,每一列点的数目为定值,那么就转化成了序列上的问题。记每一列上点的数目为 v i v_i vi,前缀和为 s i s_i si l i l_i li表示最大的 j j j,使得 s i − s j ≥ k s_i-s_j\ge k sisjk。那么答案就是 ∑ ( l i + 1 ) \sum (l_i+1) (li+1)

考虑一个稀奇古怪的做法。固定下边界后,每次插入一个元素,相当于让 v i v_i vi增加 1 1 1,然后维护对应的 { l i } \{l_i\} {li}数组。题目给的提示也非常明显,插入的总点数 ≤ 3000 \le 3000 3000。应当注意到,我们只需要维护非零位置处的取值,因此可以在 O ( k ) O(k) O(k)时间内完成修改。然后就做完了。

复杂度 O ( n 2 k ) O(n^2k) O(n2k)。具体实现细节可能要再想一下。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
int n,m,cnt,K,L[3005],R[3005],l[3005],v[3005],sz[3005];
ll res,tot;
vector<int>vec[3005];
vector<int>points;
int calc(int x){
    return l[x]*(R[x]-x);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>cnt>>K;
    for(int i=1;i<=cnt;i++){
        int x,y;cin>>x>>y;
        vec[x].pb(y);
    }
    for(int i=1;i<=n;i++){
        memset(v,0,sizeof v);
        for(int j=1;j<=i;j++){
            for(auto x:vec[j]){
                v[x]++;
            }
        }
        points.clear();
        points.pb(0);
        for(int j=1;j<=m;j++){
            if(v[j]){
                points.pb(j);
            }
        }
        points.pb(m+1);
        tot=0;R[0]=points[1];
        for(int j=1;j+1<points.size();j++){
            int x=points[j];
            L[x]=points[j-1],R[x]=points[j+1];
            l[x]=l[points[j-1]];
            sz[x]=sz[points[j-1]]+v[x];
            while(sz[x]-v[l[x]]>=K){
                sz[x]-=v[l[x]];
                l[x]=R[l[x]];
            }
            tot+=calc(x);
        }
        for(int j=1;j<=i;j++){
            res+=tot;
            for(auto x:vec[j]){
                if(--v[x]==0){
                    tot-=calc(L[x]);
                    tot-=calc(x);
                    R[L[x]]=R[x],L[R[x]]=L[x];
                    tot+=calc(L[x]);
                    for(int k=R[x];k<=m&&l[k]<=x;k=R[k]){
                        sz[k]--;
                        tot-=calc(k);
                        while(l[k]&&sz[k]<K){
                            l[k]=L[l[k]],sz[k]+=v[l[k]];
                        }
                        tot+=calc(k);
                    }
                }
                else{
                    for(int k=x;k<=m&&l[k]<=x;k=R[k]){
                        sz[k]--;
                        tot-=calc(k);
                        while(l[k]&&sz[k]<K){
                            l[k]=L[l[k]],sz[k]+=v[l[k]];
                        }
                        tot+=calc(k);
                    }
                }
            }
        }
    }
    cout<<res;
}
posted @ 2023-05-15 16:38  仰望星空的蚂蚁  阅读(24)  评论(0)    收藏  举报  来源