题意
给出nn 个物品 每个物品有一个权值wiwi
定义一个集合SS 的权值
定义一个划分的权值为W′(R)=∑S⊆RW(S)W′(R)=∑S⊆RW(S)
求将nn 个物品划分成 个集合的所有方案的权值和
n,k≤2×105,wi≤109n,k≤2×105,wi≤109
题解
考虑把每一个元素的贡献加起来
不难发现每个元素前的系数都是一样的,,设系数是
考虑枚举ii所在的集合的大小表示第二了斯特林数),),那么
考虑到Smn=1m!∑i=0m(−1)i(mi)(m−i)nSnm=1m!∑i=0m(−1)i(mi)(m−i)n
考虑∑ns=1s(n−1s−1)(k−i−1)n−s∑s=1ns(n−1s−1)(k−i−1)n−s
考虑到k(nk)=n(n−1k−1)k(nk)=n(n−1k−1)
考虑到∑i=0n(ni)(k−1)n−i=kn∑i=0n(ni)(k−1)n−i=kn
O(klogk)O(klogk)递推即可
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=2e5+5,P=1e9+7;
typedef int arr[N];
typedef long long ll;
int n,k,Sum;arr fac,ifac;ll p;
inline int pls(int a,int b){return a+=b,a<P?a:a-P;}
inline int fpm(int a,int b){if(b<0)return 1;int x=1;for(;b;b>>=1,a=(ll)a*a%P)if(b&1)x=(ll)x*a%P;return x;}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
sd(n),sd(k);int x;
fp(i,1,n)sd(x),Sum=pls(Sum,x);
fac[0]=1;fp(i,1,n)fac[i]=(ll)fac[i-1]*i%P;
ifac[n]=fpm(fac[n],P-2);fd(i,n,0)ifac[i-1]=(ll)ifac[i]*i%P;
fp(i,0,k-1)p+=(i&1?-1:1)*(ll)ifac[i]*ifac[k-i-1]%P*fpm(k-i,n-2)%P*(k-i+n-1)%P;
printf("%lld\n",(ll)pls(p%P,P)*Sum%P);
return 0;
}
然后你可以发现这玩意根本推不出来好吗,于是就有另外一种比较容易的方法
注意到W(S)=|S|∑x∈Swx,W(S)=|S|∑x∈Swx,前面的系数是|S|,|S|,对于i∈Si∈S可以当做|S||S|中每个数都对ii产生了一次贡献
那么我们只要枚举每一个数对的贡献即可
ii对自身的贡献显然是
j≠ij≠i时,j,j对ii的贡献是和ii在同一个集合的方案数也就是Skn−1Sn−1k
于是p=Skn+(n−1)Skn−1p=Snk+(n−1)Sn−1k其实也可以从上面那些东西推出来的
用Smn=1m!∑i=0m(−1)i(mi)(m−i)n=∑i=0m(−1)ii!(m−i)n(m−i)!,O(klogk)Snm=1m!∑i=0m(−1)i(mi)(m−i)n=∑i=0m(−1)ii!(m−i)n(m−i)!,O(klogk)算就好了
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=2e5+5,P=1e9+7;
typedef int arr[N];
typedef long long ll;
int n,k,Ans;arr fac,ifac;
inline int pls(int a,int b){return a+=b,a<P?a:a-P;}
inline int fpm(int a,int b){int x=1;for(;b;b>>=1,a=(ll)a*a%P)if(b&1)x=(ll)x*a%P;return x;}
inline int S(int n,int m){
ll tp=0;
fp(i,0,m)tp+=(i&1?-1:1)*(ll)fpm(m-i,n)*ifac[m-i]%P*ifac[i]%P;
return pls(tp%P,P);
}
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
sd(n),sd(k);int x;
fp(i,1,n)sd(x),Ans=pls(Ans,x);
fac[0]=1;fp(i,1,n)fac[i]=(ll)fac[i-1]*i%P;
ifac[n]=fpm(fac[n],P-2);fd(i,n,0)ifac[i-1]=(ll)ifac[i]*i%P;
Ans=(ll)Ans*pls(S(n,k),(ll)(n-1)*S(n-1,k)%P)%P;
printf("%d\n",Ans);
return 0;
}

本文探讨了一个关于组合数学的问题,具体是在给定物品数量和集合数量的情况下,计算所有可能的集合划分方式的权值总和。通过巧妙地利用斯特林数和组合恒等式,提出了一种高效算法来解决该问题。
1292

被折叠的 条评论
为什么被折叠?



