gym102460 F Miss Sloane 2019ICPC Taipei

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;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/108543149
今日推荐