zoukankan      html  css  js  c++  java
  • 【DP/数学】【CF1061C】 Multiplicity

    Description

    给定一个序列 (a),求有多少非空序列 (b) 满足 (b)(a) 的子序列并且 (forall~k~in~[1,len_b],~~k mid b_k),其中 (len_b)(b) 的长度。答案对 (1e9+7) 取模

    Input

    第一行是序列 (a) 的长度 (n)

    下面一行 (n) 个整数,代表序列 (a)

    Output

    输出一行一个整数代表答案

    Hint

    (1~leq~n~leq~100000~,~1~leq~a_i~leq~10^6)

    Solution

    总觉得这题是假的= =

    考虑DP。我们设 (f_{i,j}) 为考虑 (a) 中前 (i) 个数,填充到 (b)(j) 个的方案数

    方程显然:

    (j~ mid~a_i) 时:

    [f_{i,j}~=~f_{i - 1, j} ]

    否则:

    [f_{i,j}~=~f_{i - 1}{j}~+~f_{i - 1}{j - 1} ]

    考虑这个方程的状态是 (O(n~max(a_i))) 的,显然过不去,于是考虑优化:

    我们发现能取到第二条转移方程当且仅当 (j)(a_i) 的因数,而第一种第一种转移方程可以使用滚动数组直接省略掉。于是我们直接枚举 (a_i) 的因数,只在因数位置进行转移。同时注意因为 (i) 相同时的 (f) 时不能互相影响,所以对因数的转移要从大到小进行

    考虑这么做的复杂度:

    一共有 (n) 个数,每个数的因数是 (O(sqrt{a_i})),同时排序需要 (O(sqrt {a_i}~ imes~log (sqrt{a_i}))),总复杂度 (O(n~sqrt{a_i}~log (sqrt{a_i})))

    看起来根本过不去有木有= =

    但是考虑因数个数事实上是一个很松的上界,经过实际测试,([1,10^6]) 范围内因数个数最多的数的因数不过 (240) 个,测试结果如下:

    qwq

    于是本题的实际复杂度为 (Theta(n~ imes~d(a_i)~log (d(a_i)))),其中 (d(a_i)~leq~240)

    于是就可以轻松通过本题辣

    Code

    #include <cmath>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #ifdef ONLINE_JUDGE
    #define freopen(a, b, c)
    #endif
    #define rg register
    #define ci const int
    #define cl const long long
    
    typedef long long int ll;
    
    namespace IPT {
    	const int L = 1000000;
    	char buf[L], *front=buf, *end=buf;
    	char GetChar() {
    		if (front == end) {
    			end = buf + fread(front = buf, 1, L, stdin);
    			if (front == end) return -1;
    		}
    		return *(front++);
    	}
    }
    
    template <typename T>
    inline void qr(T &x) {
    	rg char ch = IPT::GetChar(), lst = ' ';
    	while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    	while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    	if (lst == '-') x = -x;
    }
    
    template <typename T>
    inline void ReadDb(T &x) {
    	rg char ch = IPT::GetChar(), lst = ' ';
    	while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
    	while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
    	if (ch == '.') {
    		ch = IPT::GetChar();
    		double base = 1;
    		while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
    	}
    	if (lst == '-') x = -x;
    }
    
    namespace OPT {
    	char buf[120];
    }
    
    template <typename T>
    inline void qw(T x, const char aft, const bool pt) {
    	if (x < 0) {x = -x, putchar('-');}
    	rg int top=0;
    	do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
    	while (top) putchar(OPT::buf[top--]);
    	if (pt) putchar(aft);
    }
    
    const int maxn = 100010;
    const int maxt = 1000010;
    const int MOD = 1000000007;
    
    int n;
    int MU[maxn];
    ll frog[maxt];
    std::vector<int>p;
    
    int main() {
    	freopen("1.in", "r", stdin);
    	qr(n);
    	for (rg int i = 1; i <= n; ++i) qr(MU[i]);
    	frog[0] = 1;
    	for (rg int i = 1; i <= n; ++i) {
    		p.clear();
    		for (rg int j = 1, sn = sqrt(MU[i]); j <= sn; ++j) if (!(MU[i] % j)) {
    			int k = MU[i] / j;
    			p.push_back(k); 
    			if (k != j) p.push_back(j);
    		}
    		std::sort(p.begin(), p.end());
    		for (rg int j = p.size() - 1; ~j; --j) {
    			frog[p[j]] = (frog[p[j]] + frog[p[j] - 1]) % MOD;
    		}
    	}
    	ll ans = 0;
    	for (rg int i = 1; i <= n; ++i) ans = (ans + frog[i]) % MOD;
    	qw(ans, '
    ', true);
    	return 0;
    }
    

    Summary

    一个数的因数个数是 (O(sqrt{n})) 是一个非常松的上界,事实上,在100万范围内因数个数最多的数的因数不过240个。遇到更大的范围可以 (O(n ln n)) 筛出所有数的因数来取得实际个数。

  • 相关阅读:
    CSLA服务端如何使用多线程的解决方案
    一片马蜂窝文章
    VB.NET和C#之间的语法不同比较
    [软件推荐]jQuery,JavaScript,HTML,CSS,PHP,MySQL,正则表达式 CHM帮助手册
    使用jQuery.Validate进行客户端验证
    知道AutoHotKey
    数据库设计问题
    [书籍推荐]为了自己的钱包,为了自己的时间——分享一下自己的淘书经验
    策略模式4
    SQLiteHelperSQLite帮助类
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/10213228.html
Copyright © 2011-2022 走看看