ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【CF1103D】 Professional layer 题解 (状压 dp)

2022-08-12 11:04:39  阅读:172  来源: 互联网

标签:layer val CF1103D 题解 rep times int res 质因数


CF 传送门 | 洛谷传送门

状压 dp。

Solution

发现有些题解对一些细节部分没有说明,导致某些实现部分没得到证明。

约定:记题面中的因数上限 \(k\) 为 \(limit\)。

1

记所有数的最大公因数为 \(res\)。

稍加思考可以发现,如果我想使用一个数去消除掉 \(res\) 的一个质因数 \(x\),必须要除掉这个数内所有的 \(x\),才能使得之后所有数的最大公因数不含 \(x\)。

故,若想使用一个数 \(x = p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_t^{k_t}\times r\) 消去 \(res\) 中 \(p_1,\ p_2,\ \cdots,\ p_t\) 这 \(t\) 个质因数(其中 \(x\) 的 \(r\) 部分指的是 \(x\) 中除去这些素数之外的余下部分),必须要使 \(x\) 除去 \(p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_t^{k_t}\),而这步操作可行当且仅当 \(p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_t^{k_t} \leq limit\)。所以我们明白:要想使用 \(x\) 消去 \(res\) 中的一个质因数 \(p\),与 \(p\) 在 \(res\) 中的次数无关,而是与 \(p\) 在 \(x\) 中的次数有关。

显然,我们的目标就是将 \(res\) 中的每个质因数消掉。

其实这里已然可以想到使用状压 dp 来处理了,但是直接上手复杂度显然过不了,所以需要根据限制考虑优化。

2

其中,根据数据范围可知 \(res\) 的不同质因数个数上限为 \(11\)。同时,每一个数 \(x\) 与 \(res\) 有关联的只有 \(x\) 内 同时在 \(res\) 中出现的质因数 的幂。而除了这些质因数外的因数,对 \(x\) 没有贡献,故我们可以考虑将每个 \(x\) 加工,把这些没有贡献的数全部从 \(x\) 中除去。得到的这些新的数,通过暴力打表,得知其不同种类数的上限约为 \(12000\)。

约定:下文提及的“数”都是将每个 \(a_i\) 重新处理(即剔除掉无贡献部分)后得到的新序列中的数。

上述两者的上限都较小,对于:

  • ”不同质因数 \(m\) 上限为 \(11\)”:

    不难想到使用状压 dp,定义 \(f_{i,st}\),表示使用了 \(i\) 个数,把 \(st\) 中为 \(1\) 的每一位对应的 \(res\) 中的一个质因数 除去了。其中,\(i\) 的上限为 \(m\),因为最劣情况就是使用一个数消去一个质因数,即最多使用 \(m\) 个数。

  • “新数种类数 \(S\) 上限为 \(12000\)”:

    我们可以将 \(a\) 序列中的数重新分门别类,即将处理过后相同的数放到一起处理(因为它们之间的差别只剩下代价的不同)。具体地,开一个 map,一个数值对应一个 vector,而某个数值 \(val\) 对应的 vector 内存放 那些处理之后的值为 \(val\) 的数,操作一次的代价。

    这里在实现上还有一个小优化:在将每个值对应的 vector 内部从小到大排序之后,我们最多显然是使用前 \(m\) 小的那些代价,所以第 \(m\) 个代价之后的代价于我们来说都没有意义了,故可以直接 resize(m) 掉,这样之后转移状态也不用再考虑了。

    按照处理后的值分类之后,就可以分别考虑这些值对答案的贡献,即转移 dp 状态。首先预处理出对于每个状态 \(st\)(即选取出 \(res\) 的某些质因数),若想使用 \(val\) 这个值除去 \(res\) 中的这些质因数,需要除去多少。

    然后就是转移状态了,这里按照从小到大的顺序遍历每一个代价,考虑这个数对应的每个代价对答案的贡献。(一旦发现遍历到某个代价之后没法更新任何状态,立即 break 掉。)这里转移状态的方式比较特别:枚举状态 \(st\),对于每个 \(st\),找到 \(2^m-1\) 这个状态与 \(st\) 的补集 \(C\),然后遍历这个补集中的每一个非空子集 \(k\),那么在需要除去的数(即上面预处理的部分)小于 \(limit\) 的时候,即有:\(f_{i + 1,j | k} = \min(f_{i + 1,j | k},\ f_{i,j} + cst)\)(其中 \(cst\) 指的是当前遍历到的代价。)

于是最后的答案也就水到渠成了:rep(i, 1, m) ans = min(ans, f[i][(1 << m) - 1] * i);

记新数的种类数为 \(S\),那么最终复杂度就是 \(\mathcal{\text{O}}(Sm^23^m)\),跑不满,能过。

Code

#include<bits/stdc++.h>
using namespace std;
 
#define int long long
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
#define per(i, a, b) for(register int i = a; i >= b; --i)
const int maxn = 1e6 + 5;
int n, lmt, m, res, ans = 1e15;
vector <int> fac;
struct node{
    int a, c;
}p[maxn];
map <int, vector<int> > mp;
int f[12][1 << 12];
 
inline int gcd(int x, int y){
    return !y ? x : gcd(y, x % y);
}
 
inline void dp(){
    memset(f, 2, sizeof f), f[0][0] = 0;
    for(auto val : mp){ int x = val.first;/*值为x的数*/ vector <int> mul;
        sort(val.second.begin(), val.second.end());
        if(val.second.size() > m) val.second.resize(m);
        rep(i, 0, (1 << m) - 1){ int tot = 1, tmp = x;
            rep(j, 0, m - 1) if(i & (1 << j)) 
                while(!(tmp % fac[j])) tot *= fac[j], tmp /= fac[j];
            mul.push_back(tot);//tot:要想消掉这一种搭配中所有素数,x 需要除去多少 
            //这里是按顺序存放的,之后调用的时候直接用状态调用即可 
        } for(auto cst : val.second){ bool flg = 0;//从小到大每一种代价 
            per(i, m - 1, 0) rep(j, 0, (1 << m) - 1) 
                if(f[i][j] <= 1e13){
                    int C = (1 << m) - 1 - j;//该状态的补集 
                    for(int k = C; k; k = (k - 1) & C) if(mul[k] <= lmt)
                        if(f[i + 1][j | k] > f[i][j] + cst) f[i + 1][j | k] = f[i][j] + cst, flg = 1;
                } if(!flg) break;
        }
    }
}
 
signed main(){
    scanf("%lld%lld", &n, &lmt);
    rep(i, 1, n) scanf("%lld", &p[i].a),
        res = gcd(res, p[i].a);
    rep(i, 1, n) scanf("%lld", &p[i].c);
    if(!(res - 1)) return printf("0"), 0;
    rep(i, 2, 1e6) if(i <= res and !(res % i)){
        fac.push_back(i); 
        while(!(res % i)) res /= i;
    } else if(i > res) break;
    if(res - 1) fac.push_back(res);
    m = fac.size();
    rep(i, 1, n){ int nw = 1;
        rep(j, 0, m - 1) 
            while(!(p[i].a % fac[j])) nw *= fac[j], p[i].a /= fac[j];
        mp[nw].push_back(p[i].c);//把每个输入的数更新,即剔除掉没用的部分,其最终形式为 nw 
    } dp();
    rep(i, 1, m) ans = min(ans, f[i][(1 << m) - 1] * i);
    if(ans > 1e13) printf("-1"); else printf("%lld", ans);
    return 0;
}


感谢阅读。

标签:layer,val,CF1103D,题解,rep,times,int,res,质因数
来源: https://www.cnblogs.com/gsn531/p/16579003.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有