其他分享
首页 > 其他分享> > 【CF1103D】 Professional layer 题解 (状压 dp)

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

作者:互联网

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\) 重新处理(即剔除掉无贡献部分)后得到的新序列中的数。

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

于是最后的答案也就水到渠成了: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