zoukankan      html  css  js  c++  java
  • 【题解】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. 试下这个东西 ↩︎

  • 相关阅读:
    三个心态做人做学问 沧海
    成功走职场要找准自己的"快捷键" 沧海
    免费离线下载 拂晓风起
    Hibernate 获取某个表全部记录时 奇怪现象 (重复出现某个记录) 拂晓风起
    无法读取mdb 如果连接不了ACCESS mdb文件,就尝试安装MDAC 拂晓风起
    Netbeans 使用 Hibernate 逆向工程 生成hbm和pojo 拂晓风起
    如何点击单选框 radio 后面的文字,选中单选框 拂晓风起
    Java 连接access 使用access文件 不用配置 拂晓风起
    mysql下如何执行sql脚本 拂晓风起
    Hibernate配置access Hibernate 连接 access 拂晓风起
  • 原文地址:https://www.cnblogs.com/winlere/p/12702952.html
Copyright © 2011-2022 走看看