伸手可触的『今天」安抚静脉的跃动声
FutaRimeWoawaSete · · 题解
CF1103D
难点在于打表。
设所有
一个一眼的贪心:选了一个数一定要消掉至少一个质数。所以总的选择的数的个数至多为
最暴力的转移:直接枚举选每个数和这个数消掉的质因数集合。一个很 naive 的剪枝是发现对于每个等价类只用保留前
然后你发现你现在的转移很巨大多恐怖,于是你放弃思考写了这个看起来很暴力的做法发现过了。
经打表检验,所有等价类保留前
/*
先把 gcd 求出来,然后钦定每个质因数对应哪个被选择的数就行了
但是这个预处理好像很困难。容我晚上想想。
*/
#include "bits/stdc++.h"
using namespace std;
#define int long long
const int Len = 1e6 + 5 , Inf = 1e15;
int n,k,m,a[Len],e[Len],f[20][(1 << 13) + 5];
inline int read() {
char ch = getchar();
int x = 0, f = 1;
while (ch < '0' || ch > '9') ch = getchar();
while ('0' <= ch && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b , a % b);
}
vector<int> Pm;
map<int,vector<int> > vc;
map<int,vector<int> >::iterator it;
signed main()
{
n = read() , k = read();int GCD = 0;
for(int i = 1 ; i <= n ; i ++) a[i] = read() , GCD = gcd(a[i] , GCD);
for(int i = 1 ; i <= n ; i ++) e[i] = read();
if(GCD == 1) return puts("0") & 0;
for(int i = 2 ; i * i <= GCD ; i ++) while(GCD % i == 0) Pm.push_back(i) , GCD /= i;
if(GCD != 1) Pm.push_back(GCD);
sort(Pm.begin() , Pm.end());
Pm.erase(unique(Pm.begin() , Pm.end()) , Pm.end());
m = (int)Pm.size();
for(int i = 1 ; i <= n ; i ++)
{
int ns = 1;
for(int j = 0 ; j < m ; j ++) while(a[i] % Pm[j] == 0) ns = ns * Pm[j] , a[i] /= Pm[j];
vc[ns].push_back(e[i]);
}
for(int i = 0 ; i <= m ; i ++) for(int j = 0 ; j < (1 << m) ; j ++) f[i][j] = Inf;
f[0][0] = 0;
for(it = vc.begin() ; it != vc.end() ; it ++)
{
vector<int> N;N = it -> second;int w = it -> first;
sort(N.begin() , N.end());
vector<int> A;
for(int s = 0 ; s < (1 << m) ; s ++)
{
int ds = 1 , ww = w;
for(int i = 0 ; i < m ; i ++)
{
if((s >> i) & 1)
{
while(ww % Pm[i] == 0) ds = ds * Pm[i] , ww /= Pm[i];
}
}
A.push_back(ds);
}
const int SS = (int)N.size();
for(int p = 0 ; p < min(SS , m + 1) ; p ++)
{
int sw = N[p];bool bot = 0;
for(int i = m - 1 ; i >= 0 ; i --)
for(int j = 0 ; j < (1 << m) ; j ++)
{
int NT = (1 << m) - 1 - j;
//printf("%d %d\n",j,A[j]);
for(int T = NT ; T ; T = (T - 1) & NT)
{
if(A[T] <= k && f[i + 1][j | T] > f[i][j] + sw)
{
f[i + 1][j | T] = f[i][j] + sw;
bot = 1;
}
}
}
if(!bot) break;
}
}
int as = Inf;
for(int i = 1 ; i <= m ; i ++) as = min(as , f[i][(1 << m) - 1] * i);
printf("%lld\n",(as == Inf) ? -1 : as);
return 0;
}