【学习笔记】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 si−sj≥k。那么答案就是 ∑ ( 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;
}

浙公网安备 33010602011771号