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
using namespace std;
typedef long long ll;
typedef pair 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 c[1<<11];
vector d[maxl];

inline bool cmp(const node &x,const node &y)
{
	if(x.val==y.val)
	{
		if(x.e==y.e)
			return x.id1)
		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)
					c[i].pop();
			}
		l=r;
	}
	for(int i=0;i<(1<=1;k--)
			for(int s:d[i])
				for(int j=(1<

 

你可能感兴趣的:(状态压缩)