• cogs 2405 [noi2016]循环之美 杜教筛+DP


    标签(空格分隔): 数论


    题目链接1
    题目链接2
    题目链接3
    一个推论:
    一个最简分数\(\frac{x}{y}\)在k进制下是纯循环的,当且仅当\(\gcd(y,k)=1\)
    证明:
    不妨设\(x\leq y\)。最简分数即\(\gcd(x,y)=1\)。纯循环即小数位的第1位属于循环节,那么就是存在p满足\(x\equiv x\times k^p \pmod y\)即余数出现循环是从小数点对应第一位的余数开始的。
    由于\((x,y)=1\),所以当且仅当存在p使\(1 \equiv k^p \pmod y\),那么\(\frac{x}{y}\)就是纯循环的。
    而这个结论成立当且仅当\((k,y)=1\),所以得证。

    有了这个推论,我们要求的式子一目了然:

    \[ \sum_{i=1}^{n}\sum_{j=1}^{m}[(i,j)=1]\cdot[(j,k)=1] \]

    然后就是数学式子的推导了。我尽可能写的在清晰的情况下易懂。

    \[\begin{eqnarray*} ans & = & \sum_{i=1}^n\sum_{j=1}^m[(i,j)=1]\cdot[(j,k)=1]\\ & = & \sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}{\mu(d)}\cdot[(j,k)=1]\\ & = & \sum_{d=1}^{min(n,m)}\mu(d)\cdot\left\lfloor\frac{n}{d}\right\rfloor\sum_{j=1}^{m}[d|j]\cdot[(j,k)=1]\\ & = & \sum_{d=1}^{min(n,m)}\mu(d)\cdot\left\lfloor\frac{n}{d}\right\rfloor\sum_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}[(jd,k)=1]\\ & = & \sum_{d=1}^{min(n,m)}[(d,k)=1]\cdot\mu(d)\left\lfloor\frac{n}{d}\right\rfloor\sum_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}[(j,k)=1]\\ \end{eqnarray*} \]

    \(f(n)=\sum_{i=1}^{n}[(i,k)=1]\)
    根据\(\gcd(a,b)=\gcd(a+b,b)=\gcd(a+kb,b)\),我们可以知道\(f(n)=\left\lfloor\frac{n}{k}\right\rfloor\cdot f(k) + f(n\bmod k)\),所以\(f\)函数预处理\(O(k\log k)\),查询\(O(1)\)

    \[\begin{align} ans = \sum_{d=1}^{min(n,m)}[(d,k)=1]\cdot\mu(d)\left\lfloor\frac{n}{d}\right\rfloor f\left(\left\lfloor\frac{m}{d}\right\rfloor\right) \\ \end{align} \]

    \(S(n)=\sum_{i=1}^{n}[(i,k)=1]\cdot \mu(i)\),考虑通过类似洲阁筛的DP求出\(S\)
    定义\(s(i,n)\)表示小于等于\(n\)且与\(k\)的前\(i\)种质因子互质的数\(d\)\(\mu(d)\)之和,初始化\(s(i,n)=\sum_{d=1}^{n}\mu(d)\),用杜教筛即可。

    \[ s(i,n)=s(i-1,n)-\mu(p_i)\cdot s(i,\left\lfloor\frac{n}{p_i}\right\rfloor) \]

    还是比较好理解的。意思就是从与前\(i-1\)互质的所有\(\mu\)中减去不与\(i\)互质的。由于\(\mu\)不是完全积性函数,所以要求除去\(p_i\)后与前i互质而不是与前\(i-1\)互质,因此要注意第二维转移应是递增转移。

    根据\((1)\)式可得,\(s(i,n)\)函数的第二维只有\(O(\sqrt{n}+\sqrt{m})\)种取值,所以DP的复杂度是预处理\(O(n^{\frac{2}{3}})\)+DP\(O(w(k)\cdot (\sqrt{n}+\sqrt{m}))\)(\(w(k)\)表示k的不同质因子个数,由于\(k\leq 2000\),\(w(k)\)最大也就是\(4\)),也是本题最终复杂度。

    code

    #include<stdio.h>
    #include<set>
    #include<map>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    typedef long long LL;
    int n,m,K,maxn;
    int mu[1000010];
    bool flag[1000010];
    int prime[500010],tot=0;
    void Init(){
    	mu[1]=1;
    	for(int i=2;i<=maxn;++i){
    		if(!flag[i]){
    			prime[++tot]=i;
    			mu[i]=-1;
    		}
    		for(int j=1;j<=tot&&i*prime[j]<=maxn;++j){
    			flag[i*prime[j]]=true;
    			if(i%prime[j]==0) break;
    			mu[i*prime[j]]=-mu[i];
    		}
    	}
    	for(int i=1;i<=maxn;++i) mu[i]+=mu[i-1];
    }
    map<int,int> M;
    int Gcd(int a,int b){return b==0?a:Gcd(b,a%b);}
    int Mu(int x){
    	if(x<=maxn) return mu[x];
    	if(M.count(x)) return M[x];
    	int ans=0;
    	for(int i=2,j;i<=x;i=j+1){
    		j=x/(x/i);
    		ans+=(j-i+1)*Mu(x/i);
    	}
    	M[x]=1-ans;
    	return 1-ans;
    }
    #define R(x) (x<=sqr?x:cnt-(n/x)+1)
    int H[120010],cnt;
    map<int,int> R;
    set<int> P;
    int p[10],tmp,F[2010];
    inline int f(int x){return (x/K)*F[K]+F[x%K];}
    int S[120010];
    int main(){
    	freopen("cyclic.in","r",stdin);
    	freopen("cyclic.out","w",stdout);
    	
    	scanf("%d%d%d",&n,&m,&K);
    	maxn=(int)pow(n,2.0/3.0);
    	Init();
    	
    	for(int i=1;i*i<=n;++i) P.insert(i),P.insert(n/i);
    	for(int i=1;i*i<=m;++i){
    		if(i<=n) P.insert(i);
    		if(m/i<=n) P.insert(m/i);
    	}
    	for(set<int>::iterator iter=P.begin();iter!=P.end();++iter){
    		R[*iter]=++cnt;
    		H[cnt]=*iter;
    	}
    	for(int i=1;i<=cnt;++i) S[i]=Mu(H[i]);
    	for(int i=1;i<=K;++i) F[i]=F[i-1]+(Gcd(i,K)==1);
    	int x=K;
    	for(int i=2;i*i<=x;++i){
    		if(x%i==0){
    			p[++tmp]=i;
    			while(x%i==0) x/=i;
    		}
    	}
    	if(x>1) p[++tmp]=x;
    	for(int i=1;i<=tmp;++i){
    		for(int j=1;j<=cnt;++j){
    			if(H[j]<p[i]) continue;
    			S[j]+=S[R[H[j]/p[i]]];
    		}
    	}
    	LL Last=0,Now,ans=0;
    	for(int i=1,j,y=min(n,m);i<=y;i=j+1){
    		j=min(n/(n/i),m/(m/i));
    		Now=S[R[j]];
    		ans+=(LL)(Now-Last)*(n/i)*f(m/i);
    		Last=Now;
    	}
    	printf("%lld\n",ans);
    	getchar(); getchar();
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    Java中的集合类
    Java中的包装类
    Java中的多线程总结(转)
    Java开发中的23种设计模式详解 (转)
    Java中异常处理和设计
    Jmeter的ForEach控制器
    Jmeter事务控制器
    Jmeter下线程顺序启动
    Jmeter跨线程调用参数
    Jmeter使用Python
  • 原文地址:https://www.cnblogs.com/kito/p/7009060.html
Copyright © 2020-2023  润新知