zoukankan      html  css  js  c++  java
  • 一道综合数学题

    阿狸有一个可重集合(A)(A)中有(n)个元素,因此(A)(2n)个子集。对于每个(A)的子集(x),定义(f(x))的值为,使(x)的所有元素都相同的最少操作步数。操作有两种:

    • 选择(x)中的一个元素,把这个数乘上某个质数(p)

    • 选择(x)中的一个元素,把这个数除掉某个质数(p)。这个数必须是(p)的倍数。

    阿狸想知道(sumlimits_{xsubset A}f(x))的值,但是由于这个值可能很大,你只要告诉萌萌的阿狸这个数对(1,000,000,007(10^9+7))取模的值。

    前置知识

    上指标求和公式

    [sum_{k=x}^ninom{k}{x}=inom{n+1}{x+1} ]

    由数学归纳法易得。

    范德蒙德卷积公式

    [sum_{k=0}^sinom{n}{k}inom{m}{m-k}=inom{n+m}{s} ]

    考虑生成函数,(inom{n}{k})的生成函数为((1+x)^n),左侧很明显是一个卷积,则右侧的生成函数为((1+x)^{n+m}),易证。

    由对称性,得:

    [sum_{k=0}^ninom{n}{k}inom{m}{k}=inom{n+m}{n} ]

    解决

    显然,不同质因数之间可以分开处理。考虑对于一个质因子,求出这(n)个数中这个质因子的幂次,并排序,得到序列(a)

    容易发现,问题就是要求

    [minleft{sum_{k=1}^nleft|x-a_k ight| ight} ]

    显然,取中位数为最佳。换言之,从最左和最右的两个数开始,依次向内配对。

    考虑所有子集中每一个点对的贡献:两侧所取的数的个数必须相等,中间的数可以任意取。则有:

    [egin{align*} S&=sum_{i=1}^nsum_{j=i}^n(a_j-a_i)2^{j-i-1}sum_{k=0}^ninom{i-1}{k}inom{n-j}{k}\ &=sum_{i=1}^nsum_{j=i}^n(a_j-a_i)2^{j-1-1}inom{i-1+n-j}{i-1} end{align*} ]

    改为枚举(s=i-1+n-j),有:

    [egin{align*} S&=sum_{s=0}^{n-1}sum_{i=0}^s(a_{n-s+i}-a_{i+1})2^{n-s-2}inom{s}{i}\ end{align*} ]

    减号两侧分开处理。

    左侧

    [egin{align*} S_0&=sum_{s=0}^{n-1}sum_{i=0}^sa_{n-s+i}2^{n-s-2}inom{s}{i}\ &=sum_{s=0}^{n-1}sum_{i=0}^sa_{n-i}2^{n-s-2}inom{s}{i}\ &=sum_{i=0}^{n-1}a_{n-i}sum_{s=i}^{n-1}2^{n-s-2}inom{s}{i}\ &=sum_{i=1}^{n}a_{i}sum_{s=n-i}^{n-1}2^{n-s-2}inom{s}{n-i}\ &=sum_{i=1}^{n}a_{i}sum_{s=1}^{i}2^{s-2}inom{n-s}{n-i}\ end{align*} ]

    [f_x=sum_{s=1}^{n-x}2^{s-2}inom{n-s}{x} ]

    [S_0=sum_{i=1}^na_if_{n-i} ]

    右侧

    [egin{align*} S_1&=sum_{s=0}^{n-1}sum_{i=0}^sa_{i+1}2^{n-s-2}inom{s}{i}\ &=sum_{i=0}^{n-1}a_{i+1}sum_{s=i}^{n-1}2^{n-s-2}inom{s}{i}\ &=sum_{i=1}^{n}a_isum_{s=i-1}^{n-1}2^{n-s-2}inom{s}{i-1}\ &=sum_{i=1}^na_isum_{s=1}^{n-i+1}2^{s-2}inom{n-s}{i-1}\ &=sum_{i=1}^na_if_{i-1} end{align*} ]

    合并

    [S=S_0-S_1=sum_{i=1}^na_i(f_{n-i}-f_{i-1}) ]

    问题就转化成如何求(f_i)

    显然,这个问题没有简单的形式可以找寻。不妨观察其形式,在Pascal三角形上观察,可以尝试找出一个递归式。

    考虑一个局部:

    (f_0) (f_1)
    (4a)
    (2b) (2a)
    (c) (a+b)
    (frac{1}{2}d) (frac{1}{2}(a+b+c))

    则有

    [egin{align*} f_0&=4a+2b+c+frac{1}{2}d\ f_1&=2a+(a+b)+frac{1}{2}(a+b+c)\ &=(2+1+frac{1}{2})a+(1+frac{1}{2})b+frac{1}{2}c\ &=f_0-frac{1}{2}(a+b+c+d) end{align*} ]

    一般性的,有:

    [egin{align*} f_k&=f_{k-1}-frac{1}{2}sum_{x=k-1}^{n-1}inom{x}{k-1}\ &=f_{k-1}-frac{1}{2}inom{n}{k} end{align*} ]

    这就可以(O(n))求了。至此,问题解决。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=1e9+7,inv2=5e8+4;
    const int N=300001;
    int n,cnt,cx,pri[N],e[N];
    bool cm[N];
    ll ans,fcn,inv[N],ifc[N],c[N];
    vector<int>num[N];
    char buf[N*10];
    inline ll add(ll a,ll b){return a+b>=mod?a+b-mod:a+b;}
    inline ll cut(ll a,ll b){return a-b<0?a-b+mod:a-b;}
    inline ll mul(ll a,ll b){return a*b%mod;}
    ll fpow(ll a,ll b){ll bs=1;while(b){if(b&1)bs=mul(bs,a);a=mul(a,a);b>>=1;}return bs;}
    inline ll C(int k){return mul(fcn,mul(ifc[k],ifc[n-k]));}
    void init(){
    	for(int i=2;i<N;i++){
    		if(!cm[i]){pri[++cnt]=i;e[i]=cnt;}
    		for(int j=1;j<=cnt&&i*pri[j]<N;j++){
    			cm[i*pri[j]]=true;
    			e[i*pri[j]]=j;
    			if(i%pri[j]==0)break;
    		}
    	}
    	fcn=inv[1]=ifc[0]=1;
    	for(int i=1;i<=n;i++)fcn=mul(fcn,i);
    	for(int i=2;i<=n;i++)inv[i]=mul(mod-mod/i,inv[mod%i]);
    	for(int i=1;i<=n;i++)ifc[i]=mul(ifc[i-1],inv[i]);
    	c[0]=mul(cut(fpow(2,n),1),inv2);
    	for(int i=1;i<n;i++)c[i]=cut(c[i-1],mul(C(i),inv2));
    }
    inline ll f(int k){return cut(c[n-k],c[k-1]);}
    inline int rd(){
    	int s=0;
    	while(buf[cx]<'0'||buf[cx]>'9')++cx;
    	while('0'<=buf[cx]&&buf[cx]<='9')s=10*s+buf[cx++]-'0';
    	return s;
    }
    int main(){
    	fread(buf,sizeof(char),N*10,stdin);
    	n=rd();
    	init();
    	for(int i=1;i<=n;i++){
    		int a=rd();
    		while(a>1){
    			int x=e[a],s=0;
    			while(a%pri[x]==0){a/=pri[x];s++;}
    			num[x].push_back(s);
    		}
    	}
    	for(int i=1;i<=cnt;i++){
    		int s=num[i].size();
    		sort(num[i].begin(),num[i].end());
    		for(int j=0;j<s;j++)ans=add(ans,mul(num[i][j],f(n-s+j+1)));
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    Vue动画操作
    js this
    flask 操作mysql的两种方式-sqlalchemy操作
    flask 操作mysql的两种方式-sql操作
    ansible批量加用户
    使用 WTForms 进行表单验证的例子
    flask开发用户管理系统wtf版
    [转]Python格式化输出
    scrapy中response.body 与 response.text区别
    flask开发表单
  • 原文地址:https://www.cnblogs.com/eztjy/p/13863603.html
Copyright © 2011-2022 走看看