「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;
}
在繁华中沉淀自我,在乱世中静静伫立,一笔一划,雕刻时光。