• 4.8 省选模拟赛 魔法 扩展卢卡斯 逆元 分解质因数


    avatar
    avatar

    还是太菜了 没有察觉到自己常数大。

    可以简单的发现每个数字对答案的风险为 C(n-1,i-1).

    对于模数为质数 显然可以卢卡斯定理。

    对于模数不为质数 显然可以分解质因数使用扩展卢卡斯定理(分解质因数+求阶乘+exgcd+CRT.

    const ll MAXN=1000010,maxn=11,N=400010;
    ll n,mod,top,M,MM,xx,yy;
    ll a[N];
    ll w[maxn],p[maxn];
    ll ans[maxn][N];
    ll fac[MAXN];
    inline void exgcd(ll a,ll b)
    {
    	if(!b){xx=1;yy=0;return;}
    	exgcd(b,a%b);
    	ll zz=xx;xx=yy;yy=zz-a/b*yy;
    }
    inline ll get_c(ll x,ll xx,ll xxx)
    {
    	ll cnt=0,ww=1;
    	while(x>=ww)
    	{
    		ww=ww*MM;
    		cnt+=x/ww;
    		cnt-=xx/ww;
    		cnt-=xxx/ww;
    	}
    	return cnt;
    }
    inline void prepare()
    {
    	fac[0]=1;
    	rep(1,M,i)if(i%MM)fac[i]=fac[i-1]*i%M;
    	else fac[i]=fac[i-1];
    }
    inline ll ksm(ll b,ll p,ll mod)
    {
    	ll cnt=1;
    	while(p)
    	{
    		if(p&1)cnt=cnt*b%mod;
    		p=p>>1;b=b*b%mod;
    	}
    	return cnt;
    }
    inline ll get_ans(ll a)
    {
    	if(a<=MM)return fac[a];
    	return ksm(fac[M],a/M,M)%M*fac[a%M]%M*get_ans(a/MM)%M;
    }
    inline ll inv(ll a,ll b)
    {
    	exgcd(a,b);
    	return (xx%b+b)%b;
    }
    inline ll solve(ll a,ll b)//求出 C(a,b)%M;
    {
    	ll w1=get_c(a,b,a-b);
    	ll c1=get_ans(a);//求出a!%M
    	ll c2=get_ans(b);
    	ll c3=get_ans(a-b);
    	return c1*inv(c2,M)%M*inv(c3,M)%M*ksm(MM,w1,M)%M;
    }
    signed main()
    {
    	freopen("magic.in","r",stdin);
    	freopen("magic.out","w",stdout);
    	get(n);get(mod);
    	rep(1,n,i)get(a[i]);
    	ll cc=mod;
    	for(ll i=2;i*i<=cc;++i)
    	{
    		if(cc%i==0)
    		{
    			p[++top]=i;
    			w[top]=1;
    			while(cc%i==0)
    			{
    				cc/=i;
    				w[top]*=i;
    			}
    		}
    	}
    	if(cc>1)p[++top]=w[top]=cc;
    	rep(1,top,i)
    	{
    		M=w[i];MM=p[i];
    		prepare();
    		rep(0,n-1,j)ans[i][j+1]=solve(n-1,j);
    	}
    	ll sum=0;
    	rep(1,n,i)
    	{
    		ll cnt=0;
    		rep(1,top,j)
    		{
    			M=mod/w[j];
    			ll ww=inv(M,w[j]);
    			cnt=(cnt+ww*M%mod*ans[j][i])%mod;
    			//cout<<ans[j][i]<<endl;
    		}
    		//cout<<cnt<<endl;
    		sum=(sum+cnt*a[i])%mod;
    	}
    	putl(sum);return 0;
    }
    

    复杂度为分解的(质因数个数cdot nlog^2.)

    由于全部开ll 了所以常数大的一匹 喜提比暴力分还低的40分.

    (我们直接暴力都比这个分数高。

    很容易考虑到另外一种思路 逐项计算C(n,m) 我们已知 C(n,m-1) 推出C(n,m)即可。

    当然很好推 (C(n,m)=C(n,m-1)cdot frac{n-m+1}{m})

    发现 m和mod 可能不互质 这就很不爽.

    考虑mod是一个质数的时候 利用质因数分解约分法 就是说把分子分母同时对于mod来分解 继续约分 然后约过分之后发现分母和mod互质 求逆元即可。

    不是质数的时候 质因数分解一下 采用刚才的套路 对所有分解出来的质数进行类似于这种的约分 最后是互质的求逆元即可。

    这里有一个trick 求逆元的时候 由于不一定满足费马小定理 所以需要exgcd 但是也可以求出来 mod的欧拉函数 采用欧拉定理来求逆元。

    复杂度(质因子个数cdot nlogn)

    onst int maxn=11;
    int n,mod,top;
    int w[maxn],p[maxn],c[maxn];
    inline int ksm(ll b,int p)
    {
    	ll cnt=1;
    	while(p)
    	{
    		if(p&1)cnt=cnt*b%mod;
    		b=b*b%mod;p=p>>1;
    	}
    	return cnt;
    }
    inline int insert(int x,int y)
    {
    	rep(1,top,i)while(x%p[i]==0)x/=p[i],c[i]+=y;
    	return x;
    }
    signed main()
    {
    	freopen("1.in","r",stdin);
    	//freopen("magic.out","w",stdout);
    	get(n);get(mod);
    	int s=mod,phi=mod;
    	for(int i=2;i*i<=s;++i)
    	{
    		if(s%i==0)
    		{
    			p[++top]=i;
    			phi=phi/i*(i-1);
    			while(s%i==0)s/=i,++w[top];
    		}
    	}
    	if(s>1)p[++top]=s,w[top]=1,phi=phi/s*(s-1);
    	ll ans=1,sum=0;//ans表示组合数
    	rep(1,n,i)
    	{
    		ll ww=ans;
    		rep(1,top,j)ww=ww*ksm(p[j],c[j])%mod;
    		sum=(sum+ww*read())%mod;
    		if(i!=n)ans=ans*insert(n-i,1)%mod*ksm(insert(i,-1),phi-1)%mod;
    	}
    	put(sum);
    	return 0;
    }
    

    题解上给的则是逐项计算 然后对于每个质因子采用上述约分法 但是对于某个质因子单独算出答案 最后采用CRT合并。

    复杂度和上面那个复杂度差不多不过要麻烦一点。

    还是一个没脑子选手。

  • 相关阅读:
    QML中的信号和函数
    Hangfire入门(任务调度)
    js 批量设置css样式
    phpstudy配置伪静态的方法
    mysql 文件导入方法总结
    通过配置Mysql参数提高写入速度
    phpmyadmin导入数据库大小限制修改
    易语言入门之一 简介
    判断移动设备、触屏
    Compiler Error Message: CS0016: Could not write to output file 回绝访问
  • 原文地址:https://www.cnblogs.com/chdy/p/12659891.html
Copyright © 2020-2023  润新知