• 【题解】TopCoder 11351 TheCowDivOne (单位根反演)


    【题解】TopCoder 11351 TheCowDivOne (单位根反演)

    传送门

    题目大意:

    你有(n)头牛,编号(0sim n-1)。问你从中选出(k)头牛,且选出牛的编号的和为(n)的倍数,问有多少方案,答案对1e9+7取模。(kle n le 10^9)

    这个可以用一个二元生成函数表示

    [ans=sum_i^{n(n-1)over 2} [n|i][x^i][y^k]prod_{j=0}^{n-1} (1+x^jy) ]

    (f(x)=[y^k]prod_{j=0}^{n-1} (1+x^jy)),直接考虑单位根反演,原式等于

    [=sum_{i=0}{1over n}f(omega_n^i) ]

    然而现在还是不好搞,我们把(f(x))展开

    [=[y^k]{1over n} sum_{i=0} prod_j^{n-1} (1+omega_n^{ij}y) ]

    根据单位根的性质,可以发现((1+omega_n^{ij}))是有循环节的,这个的循环节的情况等价于$ ext{for n>j>=0 , } i imes j mod n $的情况。

    一个好结论:[1]

    (a_j=ijmod n,b_j=djmod n),其中(d=gcd(i,n),i=kd)那么有(kperp n, herefore exist k^{-1} (mod n))。此外(a_j=a_{j+{nover d}})

    因此(a_j=b_{jk^{-1} mod n},b_j=a_{jk}),构成一一对应。由此,有

    [prod (1+omega_n^{ij})=prod (1+omega_n^{dj})=prod (1+omega_{nover d}^j) ]

    因此还按循环节长度(d)枚举,并且乘上贡献系数(有多少个(i)满足循环节长度是(d),也就是((i,n)={nover d},i<d)的个数,这个数量=(phi(d)))。

    [=[y^k]{1over n} sum_{d|n} phi(d)prod_{j=0}^{d-1} (1+omega_d^{j}y)^{nover d} ]

    二项式定理化开幂

    [=[y^k]{1over n} sum_{d|n} phi(d)prod_{j=0}^{d-1} sum_i^{nover d} {{nover d }choose i} omega_d^{ij} y^i ]

    (j)被孤立,交换prod

    [=[y^k]{1over n} sum_{d|n} phi(d) sum_i^{nover d} {{nover d }choose i} y^{di} prod_{j=0}^{d-1}omega_d^{ij} ]

    化简得

    [=[y^k]{1over n} sum_{d|n} phi(d) sum_i^{nover d} {{nover d }choose i} y^{di} omega_d^{{d(d-1)over 2} imes i} ]

    又因为

    [omega_d^{d(d-1)over 2}=e^{2pi i imes {d(d-1)over 2}over d}=e^{pi i(d-1)}=cos(pi(d-1))+isin(pi(d-1))=(-1)^{d+1} ]

    那么

    [={1over n} sum_{d|n} phi(d) {{nover d}choose {kover d}} (-1)^{(d+1){kover d}}[d|k] ]

    用那个根号算(phi(x)),复杂度(O(ksqrt n+n^{0.75}))

    本题主要利用了那个结论,也就是观察到这个式子只和(gcd(i,n))有关,把状态数直接将至(sqrt n)数量级

    //@winlere
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;  typedef long long ll; 
    inline int qr(){
    	int ret=0,f=0,c=getchar();
    	while(!isdigit(c))f|=c==45,c=getchar();
    	while(isdigit(c)) ret=ret*10+c-48,c=getchar();
    	return f?-ret:ret;
    }
    const int mod=1e9+7;
    const int maxn=1005;
    int inv[maxn];
    
    int phi(int x){
    	if(x==1) return 1;
    	int ret=x;
    	for(int t=2;1ll*t*t<=x;++t){
    		if(x%t==0){
    			while(x%t==0) x/=t;
    			ret-=ret/t;
    		}
    	}
    	if(x>1) ret-=ret/x;
    	return ret;
    }
    
    int c(int n,int m){
    	int ret=1;
    	for(int t=1;t<=m;++t)
    		ret=1ll*ret*(n-t+1)%mod;
    	return 1ll*ret*inv[m]%mod;
    }
    
    void pre(const int&n){
    	inv[1]=1; inv[0]=1;
    	for(int t=2;t<=n;++t) inv[t]=1ll*(mod-mod/t)*inv[mod%t]%mod;
    	for(int t=2;t<=n;++t) inv[t]=1ll*inv[t]*inv[t-1]%mod;
    }
    
    int ksm(const int&ba,const int&p){
    	int ret=1;
    	for(int t=p,b=ba;t;t>>=1,b=1ll*b*b%mod)
    		if(t&1) ret=1ll*ret*b%mod;
    	return ret;
    }
    
    int getAns(int n,int d,int k){
    	int ret=1ll*phi(d)*c(n/d,k)%mod;
    	return (1ll*(d+1)*k)%2?mod-ret:ret;
    }
    
    int main(){
    	pre(1e3);
    	int n,k;
    	while(~scanf("%d%d",&n,&k)){
    		int ans=0;
    		for(int t=1;1ll*t*t<=n;++t){
    			if(n%t==0){
    				if(k%t==0) ans=(ans+getAns(n,t,k/t))%mod;
    				if(t*t!=n&&k%(n/t)==0) ans=(ans+getAns(n,n/t,k/(n/t)))%mod;
    			}
    		}
    		ans=1ll*ans*ksm(n,mod-2)%mod;
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
    

    1. 试下这个东西 ↩︎

  • 相关阅读:
    mahout 实现canopy
    map-reduce入门
    BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第1章节--SharePoint 2013 介绍 SharePoint 2013 平台
    csu 1030: 素数槽
    ubuntu14.04上搭建android开发环境
    8 Reasons why SharePoint is Bad for Your Business 8个理由告诉你,为什么SharePoint对你的业务有害
    UVA
    【c++版数据结构】之循环单链表的实现(带头结点以及尾节点)
    HDU 1166 敌兵布阵 (树状数组)
    SQL注入式攻击
  • 原文地址:https://www.cnblogs.com/winlere/p/12702952.html
Copyright © 2020-2023  润新知