• CF961G


    首先我们考虑直接搞

    考虑每个元素的贡献,得表达式:

    $ans=sum_{i=1}^{n}w_{i}sum_{j=1}^{n}jC_{n-1}^{j-1}S(n-j,k-1)$

    即枚举每个元素所在集合中元素个数及划分方案数

    这个玩意显然是$O(n^{2})$的

    有大佬把它化简之后变成了可以直接递推的东西,但是...太恶心了好吗

    因此我们换个思想:

    我们知道,对于一种划分方式,元素$i$的贡献是$|S|w_{i}$

    那么我们可以理解为在这种划分方式下,这个集合中每个元素都产生了$w_{i}$的贡献

    那么我们分成两部分来考虑这个问题

    首先,一个元素本身无论在哪个集合里都会产生$w_{i}$的贡献,这个贡献是一定的,只与集合划分方法有关,因此这个元素本身对$w_{i}$的贡献是$w_{i}S(n,k)$

    接下来,我们考虑其他元素对这个$w_{i}$的贡献:去掉第$i$个元素之后,其他元素仍然可以划分成$k$个集合,划分方案数为$S(n-1,k)$

    在每种划分方式下,我们都可以把第$i$个元素放进任意一个集合里面去,产生的贡献等于集合大小*$w_{i}$(注意这个集合大小显然不包含$i$这个元素,因为他本身的贡献我们已经统计过了)

    这样的话,对每种划分方式,对$w_{i}$产生的贡献其实都是$(n-1)$,因为每个集合大小加起来就是$(n-1)$

    那么表达式就变成了$sum_{i=1}^{n}w_{i}[S(n,k)+(n-1)S(n-1,k)]$

    也就是$[S(n,k)+(n-1)S(n-1,k)]sum_{i=1}^{n}w_{i}$

    注意到第二类斯特林数可以$O(klog_{2}k)$递推,后面那个东西读入的时候累个前缀和即可

    第二类斯特林数的递推式:$S(n,m)=frac{1}{m!}sum_{i=0}^{m}(-1)^{i}C_{m}^{i}(m-i)^{n}$

    贴代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define ll long long
    using namespace std;
    const ll mode=1000000007;
    ll minv[200005];
    ll inv[200005];
    ll mul[200005];
    ll n,k,s;
    ll w[200005];
    void init()
    {
        minv[0]=minv[1]=inv[0]=inv[1]=mul[0]=mul[1]=1;
        for(int i=2;i<=200000;i++)
        {
            inv[i]=(mode-mode/i)*inv[mode%i]%mode;
            minv[i]=minv[i-1]*inv[i]%mode;
            mul[i]=mul[i-1]*i%mode;
        }
    }
    ll pow_mul(ll x,ll y)
    {
        ll ret=1;
        while(y)
        {
            if(y&1)ret=ret*x%mode;
            x=x*x%mode,y>>=1;
        }
        return ret;
    }
    ll C(ll x,ll y)
    {
        if(x<y)return 0;
        return mul[x]*minv[y]%mode*minv[x-y]%mode;
    }
    ll get_S(ll x,ll y)
    {
        ll ret=0,f=1;
        for(int i=0;i<=y;i++)ret=(ret+f*C(y,i)*pow_mul(y-i,x)%mode+mode)%mode,f=-f;
        return ret*minv[y]%mode;
    }
    int main()
    {
        init();
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<=n;i++)scanf("%lld",&w[i]),s=(s+w[i])%mode;
        printf("%lld
    ",s*((get_S(n,k)+(n-1)*get_S(n-1,k)%mode)%mode)%mode);
        return 0;
    }
  • 相关阅读:
    想开始学习易语言
    又是一天过去了
    希望疫情早点过去
    你们都是在哪里找买软件框架的
    Leetcode 538. 把二叉搜索树转换为累加树
    Leetcode 543. 二叉树的直径 树的遍历
    Leetcode 347. 前 K 个高频元素
    Leetcode 337. 打家劫舍 III
    工作小记:企业微信 嵌H5页面 用户权限获取匹配
    (十一)React Ant Design Pro + .Net5 WebApi:后端环境搭建IdentityServer4(三)持久化
  • 原文地址:https://www.cnblogs.com/zhangleo/p/11136549.html
Copyright © 2020-2023  润新知