zoukankan      html  css  js  c++  java
  • 【做题】SRM704 Div1 Median

    原文链接 https://www.cnblogs.com/cly-none/p/SRM704Div1B.html

    给出(n)和模数(P)(q)次询问,每次给出一个([0,P-1])范围内的整数(v),求有多少长度为(n)的序列({x})满足(x_i)都是([0,P-1])范围内的整数且(prod x_i equiv v pmod P),答案对(10^9 + 7)取模。

    (n leq 50 , q leq 10^3, P leq 10^9)

    注意:(P)不是质数。

    这题大概就是道结论题。

    首先,我们容易得到(O(n P^2))的dp。

    考虑(P)为质数的情况。我们发现,对于所有(a eq 1)({a, 2a, cdots, (P-1)a})都是互不相等的,即它在模(P)意义下就是({1, 2, cdots , P-1})。那么,在dp中(1)(P-1)的所有数的转移都是相同的,那它们的答案也应当是相等的。

    尝试把这个结论拓展到(P)为正整数的情况下。我们设模(P)意义下的每个数 $v = v' imes d $ ,其中 (d=gcd(v,P)) 。同样地,({ v', 2v', cdots , (P-1)v' })互不相等,于是 (v) 乘以(1)(P-1)的所有数就是({d, 2d, cdots , (P-1)d })。于是,我们发现(gcd(x,P))相同的(x)在dp中有相同的转移,那就可以把它们的状态合并在一起。这样这个dp就优化到了(O(n sigma (n)^2))的了,已经可以通过本题。

    但我们还可以套路地对(P)做质因数分解,得到(P = p_1^{a_1} p_2^{a_2} cdots p_n^{a_n}),然后以每个(p_i^{a_i})为模数,分别求一次答案。考虑所有模(P)意义下的数和模(p_i^{a_i})得到的序列是一一对应的,所以我们可知答案就是以每个(p_i^{a_i})为模数的答案的积。那么,就能(O(n log^2 P + q log P))地解决本题。(这里的(log P)分析并不准确)

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef double db;
    #define fir first
    #define sec second
    
    class ModEquation {
    public:
        vector <int> count( int n, int K, vector <int> query ) ;
    };
    const int MAX = 100000, MP = 30, N = 60, MOD = (int)(1e9 + 7);
    int isp[MAX + 10], pri[MAX], pcnt, fac[MP], fcnt, num[MP];
    int power(int a,int b) {
      int ret = 1;
      while (b) {
        if (b & 1) ret = 1ll * ret * a % MOD;
        a = 1ll * a * a % MOD;
        b >>= 1;
      }
      return ret;
    }
    void prework() {
      for (int i = 2 ; i <= MAX ; ++ i) {
        if (!isp[i]) pri[++pcnt] = i;
        for (int j = 1 ; pri[j] * i <= MAX ; ++ j) {
          isp[pri[j] * i] = 1;
          if (i % pri[j] == 0) break;
        }
      }
    }
    int dp[MP][N][MP];
    void init() {
      pcnt = fcnt = 0;
      memset(isp,0,sizeof isp);
      memset(dp,0,sizeof dp);
    }
    vector <int> ModEquation::count(int n, int K, vector <int> query) {
      init();
      prework();
      for (int i = 1 ; i <= pcnt ; ++ i) {
        if (K % pri[i]) continue;
        fac[++fcnt] = pri[i];
        num[fcnt] = 0;
        while (K % pri[i] == 0)
          ++ num[fcnt], K /= pri[i];
      }
      if (K != 1) {
        ++ fcnt;
        fac[fcnt] = K;
        num[fcnt] = 1;
      }
      for (int i = 1 ; i <= fcnt ; ++ i) {
        dp[i][0][0] = 1;
        for (int j = 0 ; j < n ; ++ j) {
          for (int a = 0 ; a <= num[i] ; ++ a)
            for (int b = num[i], t = 1 ; b >= 0 ; -- b, t *= fac[i]) {
              (dp[i][j+1][min(num[i], a + b)] += 1ll * dp[i][j][a] * (t - t / fac[i]) % MOD) %= MOD;
            }
        }
        for (int a = num[i], t = 1 ; a >= 0 ; -- a, t *= fac[i]) {
          dp[i][n][a] = 1ll * dp[i][n][a] * power(t - t / fac[i], MOD - 2) % MOD;
        }
      }
      vector<int> ans = vector<int>();
      for (int id = 0 ; id < (int)query.size() ; ++ id) {
        int v = query[id], ret = 1;
        for (int i = 1 ; i <= fcnt ; ++ i) {
          int tmp = v, rec = 0;
          for (int j = 1 ; j <= num[i] ; ++ j)
    				if (tmp % fac[i] == 0) tmp /= fac[i], ++ rec;
          ret = 1ll * ret * dp[i][n][rec] % MOD;
        }
        ans.push_back(ret);
      }
      return ans;
    }
    
    #undef fir
    #undef sec
    

    小结:数论题有一些基本结论和套路还是非常重要的。

  • 相关阅读:
    git命令_保存本地变更+拉取+合并代码+推送代码到远程仓+添加CI触发器变量
    debian_linux_apt-get命令_dpkg命令
    debian_linux系统_访问真实环境rancher_证书问题相关_https相关_使用kubectl命令行查看资源时报错:Unable to connect to the server: x509: certificate signed by unknown authority
    linux_xargs入门介绍_及和for循环命令区别
    技术实践丨GaussDB(DWS)运维管理功能“升级”的原理和使用
    华为云数据安全中心正式公测,8大核心数据安全能力守护你的数据
    华为鲲鹏专家解读:90%代码如何移植到鲲鹏平台
    数据库技术丨GaussDB(DWS)数据同步状态查看方法
    你掉进过“伪敏捷”的陷阱吗?
    拯救深度学习:标注数据不足下的深度学习方法
  • 原文地址:https://www.cnblogs.com/cly-none/p/SRM704Div1B.html
Copyright © 2011-2022 走看看