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);
}
posted @ 2022-11-03 21:47  蒟蒻丁  阅读(24)  评论(0编辑  收藏  举报