https://codeforces.com/gym/102460
学习自出题人交的标程
首先题目里面那个式子可以观察到发现是i*sum{e[i]},也就是你选了几个是这些e[i]之和乘以选的个数
然后初始gcd最大是1e12,那么最多就是11个质因子,我们的目标就是把这些gcd中所有质因子的数字给除掉,又因为一个数字只能除以一次,所以我们如果要对某个数字进行除以操作,必定是这个数字能把gcd中的某些质因子除到0,否则就是白给
所以我们最多选择不超过11个数字进行除以操作,那么就可以进行状压DP,dp[i][s]表示选用i个数字进行消除示,消除质因子情况是s,s中为1质因子已经被消除掉了的最小e之和。那么预处理的时候对于每个议员的a[i],找出他能使得哪些gcd消除到0,且用的除数在k以内,吧他的e[i]加到对应状态的一个堆里面,由于最多消除11个议员,那么消除有2^11次方种情况,每个情况也只要存最多11个就行了,
那么这2^11 * 11的较小的消除值就是可用的,但是一个议员只能消一次,所以他所有能消的gcd只能用一个,有点像分组背包,我们把他做一个状压DP,对于每一种课消除的状态枚举一次所有状态转移一次就行了,复杂度就是11*2^22
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
const int maxl=1e6+10;
const ll inf=1e15;
int n,tot;
ll k,g,ans;
ll e[maxl],p[12],b[12],ck[1<<11];
struct node
{
ll val,e;int id;
}a[maxl];
ll dp[12][1<<11];
priority_queue<pr> c[1<<11];
vector<int> d[maxl];
inline bool cmp(const node &x,const node &y)
{
if(x.val==y.val)
{
if(x.e==y.e)
return x.id<y.id;
return x.e<y.e;
}
return x.val<y.val;
}
inline void prework()
{
scanf("%d%lld",&n,&k);
g=0;tot=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i].val);
g=__gcd(g,a[i].val);
}
for(ll i=2;i*i<=g;++i)
if(g%i==0)
{
p[++tot]=i;
while(g%i==0)
g/=i;
}
if(g>1)
p[++tot]=g;
for(int i=1;i<=n;i++)
{
scanf("%lld",&e[i]);
ll a2=a[i].val;
for(int j=1;j<=tot;j++)
while(a2%p[j]==0)
a2/=p[j];
a[i].val/=a2;a[i].e=e[i];a[i].id=i;
}
sort(a+1,a+1+n,cmp);
}
inline void mainwork()
{
if(tot==0)
{
ans=0;
return;
}
ck[0]=1;int l=1,r;
while(l<=n)
{
r=l;
while(a[r].val==a[l].val && r<=n)
++r;
for(int i=1;i<=tot;i++)
{
b[i]=1;
while(a[l].val%p[i]==0)
{
b[i]*=p[i];
a[l].val/=p[i];
}
for(int j=0;j<(1<<(i-1));j++)
ck[j|(1<<(i-1))]=ck[j]*b[i];
}
for(int i=0;i<(1<<tot);i++)
if(ck[i]<=k)
for(int j=l;j<=min(r-1,l+tot-1);j++)
{
c[i].push({a[j].e,a[j].id});
if((int)c[i].size()>tot)
c[i].pop();
}
l=r;
}
for(int i=0;i<(1<<tot);i++)
while(c[i].size())
{
d[c[i].top().second].push_back(i);
c[i].pop();
}
for(int i=0;i<=tot;i++)
for(int j=0;j<(1<<tot);j++)
dp[i][j]=inf;
ans=inf;dp[0][0]=0;
for(int i=1;i<=n;i++)
for(int k=tot;k>=1;k--)
for(int s:d[i])
for(int j=(1<<tot)-1-s;;j=(j-1)&~s)
{
dp[k][j|s]=min(dp[k-1][j]+e[i],dp[k][j|s]);
if(!j) break;
}
for(int i=1;i<=tot;i++)
ans=min(i*dp[i][(1<<tot)-1],ans);
if(ans==inf)
ans=-1;
}
inline void print()
{
printf("%lld\n",ans);
}
int main()
{
prework();
mainwork();
print();
return 0;
}