zoukankan      html  css  js  c++  java
  • 2019牛客多校第三场D-Big Integer

    题意

    定义(A(n)) 为 n个1表示的十进制数,例如(A(3) = 111)

    然后对于(1 le i le n,1le j le m) 问有多少的 (pairs(i,j))满足(A(i^j) equiv 0 pmod p)

    分析

    $11cdots 111 = {10^n-1 over 9} equiv 0pmod p $

    等价于(10^n equiv 1pmod {9p})

    (p = 2,5)时,显然答案为0(因为(11cdots 111) 模2或模5肯定不是0)

    (p eq 2,5) 时,有(gcd(10,9p) = 1),有(10^{phi(9p)} equiv 1pmod {9p})

    (phi(9p))是欧拉函数,这个式子由欧拉定理可知

    所以只需要找(10^i mod ~9p) 的最小循环节d,显然 (d|phi(9p)) ,所以只需要暴力找(phi(9p))的因子,找到最小的符合条件的即可

    ”显然成立“部分证明:

    设d不是 (n = phi(9p)) 的因子,那么 (n = kd + r) , 又(10^{n} equiv 1pmod {9p}) ,(10^{d} equiv 1pmod {9p}) ,所以有(10^r equiv 1pmod {9p}),r比d小,与d最小矛盾

    接下来只需要找有多少个(pair(i,j), d|i^j)

    把 d 质因数分解:(d=p_1^{k_1}p_2^{k_2}cdots p_l^{k_l}), 要使得 (i^j)(d) 的倍数,那么在 (i^j) 的质因数分解中 (p_1,p_2cdots p_l) 的指数中都要比(d) 中的大,所以我们考虑 j 固定的时候,有多少个 i 可以满足条件。

    很容易就可以想到

    (i) 必须是 (g = p_1^{lceil {k_1over j} ceil} p_2^{lceil {k_2over j} ceil}cdots p_l^{lceil {k_lover j} ceil}) 的倍数(至于为什么上取整,可以想一想,因为要求最小的 (x), 有(x*j >= k_1 && x*(j-1) < k_1) )。因此一共有(nover g)个合法的(i)

    (mx = max(k_1,k_2,cdots k_l)) 那么 我们只需要依次计算(j,(j in [1,mx])) 就可以了。而对于 (j > mx) 的部分,合法的 i 的个数都是一样的。不妨带入上式看一看。

    计算原理就是这样。但是实际操作又遇到了一些坑..

    计算(phi(9p)) 后,枚举因数找循环节时,快速幂会爆ll,所以要用快速乘(因为p最大1e9)

    另一种方法是,因为欧拉函数是积性函数,所以如果9和p互质,那么(phi(9p) = phi(9) *(p-1)) ,对(p=3)的情况进行特判,而对于其他情况只需要枚举(phi(p))的因子即可。

    因为当9和p互质时,若有 n 对 $10^n equiv 1pmod {9p} $成立,那么一定有(10^n equiv 1pmod {p}) 成立

    代码

    计算(phi(9p))快速乘方法

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 100010;
    int n,m,p;
    int q[N],c[N],tot;
    ll mul(ll a, ll b, ll p) {
        ll ret = 0;
        while (b) {
            if (b & 1) ret = (ret + a) % p;
            b /= 2;
            a = (a + a) % p;
        }
        return ret;
    }
    ll ksm(ll a,ll b,ll mod){
        ll res = 1;
        for(;b;b>>=1){
            if(b&1)res = mul(res,a,mod);
            a = mul(a,a,mod);
        }
        return res;
    }
    ll getphi(ll x){
        ll res = x;
        for(ll i=2;i*i <= x;i++){
            if(x % i == 0){
                res = res / i * (i-1);
                while(x % i == 0)x /= i;
            }
        }
        if(x > 1)res = res / x * (x - 1);
        return res;
    }
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d%d%d",&p,&n,&m);
            if(p == 2 || p == 5){
                puts("0");
                continue;
            }
            ll phi = getphi(9ll * p);
            ll fac = 1e18;
            for(ll i=2;i*i<=phi;i++){
                if(phi % i == 0){
                    if(ksm(10,i,p * 9ll) == 1)fac = min(fac,i);
                    if(ksm(10,phi/i,p * 9ll) == 1)fac = min(fac,phi/i);
                }
            }
            tot = 0;
            for(int i=2;i*i<=fac;i++){
                if(fac % i == 0){
                    q[++tot] = i;c[tot] = 0;
                    while(fac % i == 0)fac /= i,c[tot] ++;
                }  
            }
            if(fac > 1)q[++tot] = fac,c[tot] = 1;
            ll res = 0;
            int mx = 0;
            for(int i=1;i<=tot;i++) mx = max(mx,c[i]);
            for(int j=1;j <= mx && j <= m;j++){
                int now = 1;
                for(int i=1;i<=tot;i++){
    
                    int k = c[i] / j + (c[i]%j != 0);
    
                    for(int o = 1;o<=k;o++)now *= q[i];
                }
                res += n / now;
            }
            if(m > mx){
                int now = 1;
                for(int i=1;i<=tot;i++)now *= q[i];
                res += 1ll * (m-mx) * (n / now);
            }
            printf("%lld
    ",res);
        }
        return 0;
    }
    

    标程用的方法

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    vector<pair<int, int> > plist;
    int pow_mod(int x, int k, int p)
    {
    	int ret = 1;
    	for (; k; k>>=1)
    	{
    		if (k&1) ret = 1LL*ret*x%p;
    		x = 1LL*x*x%p;
    	}
    	return ret;
    }
    int f(int n, int k)
    {
    	int d = 1;
    	for (auto pv: plist)
    	{
    	 	int t = (pv.second+k-1) / k;
    	 	while (t--) d *= pv.first;
        }
    	return n/d;
    }
    int main()
    {
        int T, n, m, p, d, D;
    	scanf("%d", &T);
    	while (T--)
    	{
    		scanf("%d %d %d", &p, &n, &m);
    		if (p == 2 || p == 5) {puts("0"); continue;}
    		if (p == 3) // 10^d = 1 mod 27 特判p = 3
    		{
    			//phi(27) = 18
    			D = 18;
    			p = 27;
    		}
    		else D = p-1;
    		assert(pow_mod(10, D, p) == 1);
    		d = 1e9;
    		for (int i = 1; i*i <= D; ++i)
    		{
    			if (D % i) continue;
    			if (pow_mod(10, i, p) == 1)
    			   d = min(d, i);
                if (pow_mod(10, D/i, p) == 1)
                   d = min(d, D/i);
    		}
    		for (int i = 2; i*i <= d; ++i)
    		{
    		 	if (d % i) continue;
    			int c = 0;
    			while (d % i == 0) ++c, d /= i;
    			plist.push_back(make_pair(i, c));	
    		}
    		if (d != 1) plist.push_back(make_pair(d, 1));
    		LL ans = 0;
    		for (int i = 1; i <= 30 && i <= m; ++i)
    			ans += f(n, i);
    		if (m > 30) ans += 1LL*(m-30)*f(n, 30);
    		printf("%lld
    ", ans);
    		plist.clear();
    	}
    	return 0;
    }
    
  • 相关阅读:
    数据结构之队列java版
    SPSS-相关性和回归分析(一元线性方程)案例解析(转)
    SPSS-非参数检验—两独立样本检验 案例解析(转)
    spss-非参数检验-K多个独立样本检验( Kruskal-Wallis检验)案例解析(转)
    SPSS-Friedman 秩和检验-非参数检验-K个相关样本检验 案例解析(转)
    SPSS-多重响应-频率和交叉表案例分析(问卷调查分析)(转)
    SPSS—回归—多元线性回归(转)
    SPSS—回归—曲线估计方程案例解析(转)
    SPSS—非线性回归(模型表达式)案例解析(转)
    SPSS—二元Logistic回归(转)
  • 原文地址:https://www.cnblogs.com/1625--H/p/11247599.html
Copyright © 2011-2022 走看看