CF912D 小鱼仔 题解
这是一个很邪门的贪心
考虑到最终答案是每个正方形的贡献除以总的正方形个数,而正方形个数容易计算,那么只需最大化贡献。
从题面给出的图易得 每个点被覆盖的次数是一定的,我们只需要在被覆盖最多的点上面放置小鱼即可让答案最大化
接着容易想到一个暴力的做法,就是 \(n^2\) 统计每个点的贡献,取前 \(k\) 个点放置小鱼。
再考虑优化,每个点被覆盖情况实际上可以看成长和宽的被覆盖情况的乘积,我们把正方形看成是两条线段,分别覆盖在长和宽上,这样的两条线段对应原矩形中唯一一个正方形。设长和宽被覆盖次数为两个序列 \(a_i\) \(b_j\) ,原矩形为 \(c_{i,j}\) ,那么 \(c_{i,j}=a_i \cdot b_j\) , 我们的任务就是计算出前 \(k\) 大的 \(c_{i,j}\) ,考虑把 \(a_i\) \(b_j\) 排序,从大到小分别枚举 \(i\) ,\(j\) 把对应的乘积放入平衡树或者 \(set\) 。由于我们是从大到小枚举,如果一个乘积 \(a_i * b_j\) 在平衡树中的排位大于 \(k\) ,继续下去的话乘积只会更小,那么他自己和他后面的元素也就都没有价值了,我们直接 \(break\) 掉,这个做法时间复杂度目测不会超过 \(O(n \sqrt{n} \log{n})\) ,实际测试跑的快得多。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
typedef long long ll;
using namespace std;
ll n,m,k,r,a[400001],b[400001],ans;
struct Splay{
#define N (ll)3e6
ll siz[N],fa[N],son[N][2],vul[N],tot,root,cnt[N];
void UP(ll x){
siz[x]=siz[son[x][0]]+siz[son[x][1]]+cnt[x];
}
bool dir(ll x){
return son[fa[x]][1]==x;
}
void rotate(ll x){
ll y=fa[x],z=fa[y],k=dir(x),w=son[x][k^1];
fa[x]=z,son[z][dir(y)]=x;
fa[y]=x,son[x][k^1]=y;
fa[w]=y,son[y][k]=w;
UP(y),UP(x);
}
void splay(ll x,ll goal=0){
while(fa[x]!=goal){
ll y=fa[x],z=fa[y];
if(z!=goal){
if(dir(x)==dir(y))rotate(y);
else rotate(x);
}
rotate(x);
}
if(!goal)root=x;
}
void find(ll x){
if(!root)return ;
ll cur=root;
while(son[cur][x>vul[cur]]&&x!=vul[cur])
cur=son[cur][x>vul[cur]];
splay(cur);
}
ll LR(ll x,ll k){
find(x);
if(vul[root]<x&&k==0)return root;
if(vul[root]>x&&k==1)return root;
ll cur=son[root][k];
while(son[cur][k^1])cur=son[cur][k^1];
return cur;
}
ll insert(ll x){
ll cur=root,p=0;
while(cur&&x!=vul[cur]){
p=cur;
cur=son[cur][x>vul[cur]];
}
if(cur)cnt[cur]++;
else {
cur=++tot;
if(p)son[p][x>vul[p]]=cur;
siz[cur]=cnt[cur]=1;
fa[cur]=p,vul[cur]=x;
son[cur][0]=son[cur][1]=0;
}
splay(cur);
}
void Delete(ll x){
ll le=LR(x,0),re=LR(x,1);
splay(le),splay(re,le);
ll del=son[re][0];
if(cnt[del]>1)cnt[del]--,splay(del);
else son[re][0]=0;
UP(re),UP(le);
}
ll get_rank(ll x){
find(x);
if(vul[root]>=x)return siz[son[root][1]]+cnt[root];
return siz[son[root][1]];
}
}T;
int main(){
T.insert(-1e15);
T.insert(1e15);
cin>>n>>m>>r>>k;
for(ll i=1;i<=n-r+1;i++)a[i]++,a[i+r]--;
for(ll i=1;i<=m-r+1;i++)b[i]++,b[i+r]--;
for(ll i=1;i<=n;i++)a[i]+=a[i-1];
for(ll i=1;i<=m;i++)b[i]+=b[i-1];
sort(1+a,1+a+n);
sort(1+b,1+b+m);
for(ll i=n;i>=1;i--){
for(ll j=m;j>=1;j--){
ll sum=a[i]*b[j];
if(T.get_rank(sum)>k+1)break;
T.insert(sum);
}
}
while(k--){
ll cur=T.LR(1e12,0);
ans+=T.vul[cur];
T.Delete(T.vul[cur]);
}
long double Ans=1.0*ans/(n-r+1)/(m-r+1);
printf("%.10Lf",Ans);
}