背景:
不是每天都有背景的。
题目传送门:
https://www.luogu.org/problem/CF961G
题意:
n
n
n个数,每一个数有一个权值
w
i
w_i
wi,现在将这些数分成
k
k
k个集合,对于一种分法的贡献是:
∑
i
=
1
k
∣
S
i
∣
∑
j
∈
S
i
w
j
\sum_{i=1}^{k}|S_i|\sum_{j∈S_i}w_j
∑i=1k∣Si∣∑j∈Siwj,求最后的贡献和。
具体可见样例解释。
思路 1 1 1:
a n s = ∑ i = 1 n w i ∑ j = 1 n j C n − 1 j − 1 S n − j , k − 1 ans=\sum_{i=1}^{n}w_i\sum_{j=1}^{n}jC_{n-1}^{j-1}S_{n-j,k-1} ans=i=1∑nwij=1∑njCn−1j−1Sn−j,k−1
理解起来也不难,就是枚举当前第
i
i
i个数所在的集合大小
j
j
j。
那么可以在剩下的
n
−
1
n-1
n−1个数中选
j
−
1
j-1
j−1个数放在这个集合中,方案数为
C
n
−
1
j
−
1
C_{n-1}^{j-1}
Cn−1j−1;
而剩下的
n
−
j
n-j
n−j个数就必须放在剩下的
k
−
1
k-1
k−1个集合中,方案数为
S
n
−
j
,
k
−
1
S_{n-j,k-1}
Sn−j,k−1。
最后乘上当前集合的大小即可。
可是太菜了不会化简式子。可前往大佬们的博客查找。
思路 2 2 2:
容易发现答案的形式形如
p
∑
i
=
1
n
w
i
p\sum_{i=1}^{n}w_i
p∑i=1nwi,其中
p
p
p为一个系数,就是我们要算的。
考虑其它的数
i
i
i对当前的数
x
x
x的贡献。
若
x
=
i
x=i
x=i,则可以任意选择,因此方案数为
S
n
,
k
S_{n,k}
Sn,k;
若
x
≠
i
x≠i
x̸=i,当且仅当
x
x
x和
i
i
i在同一个人集合时才会有贡献,因此在剩下的
n
−
1
n-1
n−1个数中选择一个数
i
i
i,方案数为
n
−
1
n-1
n−1;剩下的
n
−
1
n-1
n−1个数(除了
x
x
x以外的数)本身就可以组成
k
k
k(不可能组成
k
−
1
k-1
k−1个集合,因为此时
x
≠
i
x≠i
x̸=i,自己对自己的贡献已经被计算过了)个集合,方案数为
S
n
−
1
,
k
S_{n-1,k}
Sn−1,k。
因此有:
a
n
s
=
(
S
n
,
k
+
(
n
−
1
)
S
n
−
1
,
k
)
∑
i
=
1
n
w
i
ans=\big(S_{n,k}+(n-1)S_{n-1,k}\big)\sum_{i=1}^{n}w_i
ans=(Sn,k+(n−1)Sn−1,k)i=1∑nwi
考虑
S
n
,
m
=
∑
i
=
0
m
(
−
1
)
i
i
!
(
m
−
i
)
n
(
m
−
i
)
!
S_{n,m}=\sum_{i=0}^{m}\frac{(-1)^i}{i!}\frac{(m-i)^{n}}{(m-i)!}
Sn,m=∑i=0mi!(−1)i(m−i)!(m−i)n,
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn)求解即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod 1000000007
using namespace std;
int n,k;
int fac[200010],inv[200010],Inv[200010];
int sum=0;
void init()
{
fac[0]=fac[1]=1;
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)
{
fac[i]=(LL)fac[i-1]*i%mod;
inv[i]=((LL)mod-mod/i)*inv[mod%i]%mod;
}
Inv[0]=Inv[1]=1;
for(int i=2;i<=n;i++)
Inv[i]=(LL)Inv[i-1]*inv[i]%mod;
}
int ksm(int x,int k)
{
int tot=1;
for(;k;k>>=1)
{
if(k&1) tot=(LL)tot*x%mod;
x=(LL)x*x%mod;
}
return tot;
}
int calc_S(int n,int m)
{
int sum=0;
for(int i=0;i<=m;i++)
sum=((LL)sum+((i&1)?-1ll:1ll)*Inv[i]*ksm(m-i,n)%mod*Inv[m-i]%mod+mod)%mod;
return sum;
}
int main()
{
int x;
scanf("%d %d",&n,&k);
init();
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
sum=((LL)sum+x)%mod;
}
printf("%d",((LL)calc_S(n,k)+(LL)(n-1)*calc_S(n-1,k)%mod)%mod*sum%mod);
}