「CF1380G」 Circular Dungeon

CF1380G Circular Dungeon

看懂样例就能做。

虽然我瞪了 20 分钟 菜是原罪

首先可以将从每一个点出发所能获得的价值相加,再除以 \(n\) 就可以得到价值的期望。

所以问题转化为使从每一个点出发所能获得的价值之和最小。

有一个显而易见的结论:我们一定会将价值前 \(k\) 大的宝箱变成假宝箱。

为了尽可能减小价值之和,这些假宝箱一定不会相邻,于是这 \(k\) 个假宝箱把这个环分成了 \(k\) 段。

为了方便起见,你可以把环断开成链,以某个假宝箱为断点,每一段都是完整的。

那么问题就变成了将真宝箱分为 \(k\) 段,使得从每一个点出发所能获得的价值之和最小。

显然在同一段的点只能获得自己这一段中宝箱的价值。

考虑在同一段的每个点出发所能获得的总价值,设这一段宝箱中第 \(i\) 个宝箱的价值为 \(v_i\)

则有

\[sum=\sum_{i=1}^niv_i \]

那么显然这一段中的宝箱价值应按照降序排序才能保证总价值最小。

我们刚才考虑了一段的总价值,现在我们从总体上看,怎样能够使得价值最小?

注意到每一段的第一个数会对答案产生一次贡献,每一段的第二个数会对答案产生二次贡献,...,也就是说我们需要保证每一段的第一个数尽可能大,这样对答案的贡献才会小。

即设一共有 \(k\) 段,我们可以将真宝箱中价值前 \(k\) 大的宝箱放在每一段的第一个,剩余真宝箱中的前 \(k\) 大放在每一段的第二个,以此类推,不足 \(k\) 个可以看作用 \(0\) 补齐。

这样这个题就做完了。

总时间复杂度为 \(O(n\log_2n)\)

(其实你看我说这么大一堆代码超简单的)

#include<bits/stdc++.h>
using namespace std;
const int p=998244353;
const int maxn=3e5+5;
int a[maxn],sum[maxn];
int ksm(int a,int b,int p){
	int ans=1;
	while(b){
		if(b&1) ans=1ll*ans*a%p;
		b>>=1,a=1ll*a*a%p;
	}
	return ans;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int n;cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	sort(a+1,a+n+1);
	reverse(a+1,a+n+1);
	for(int i=1;i<=n;++i) sum[i]=(sum[i-1]+a[i])%p;
	int inv=ksm(n,p-2,p);
	for(int k=1;k<=n;++k){
		int ans=0;
		for(int j=0,i=0;i<n;++j,i+=k)
			ans=(ans+1ll*j*(sum[min(n,i+k)]-sum[i]+p)%p)%p;
		cout<<1ll*ans*inv%p<<' ';
	}
	return 0;
}
posted @ 2020-09-11 11:05  Henry__Huang  阅读(228)  评论(0编辑  收藏  举报