CQOI2016 伪光滑数 可并堆+搜索

大意:(不好描述,看题目吧)

    时间复杂度,一定是要依托于k来计算,看似要枚举的东西很乱,细细的规划一下,可以想到把他们以一个二元组区分(p,size)

表示一个数的最大素数为p,且为size个素数相乘,一开始预处理,就可以求出2-127这31个质数的最大size,即 pi^size[i]>=N(size[i]取到最小)(这样能保证之后的所有状态的权值都是小于N的),一开始,我们的状态数就只有sigma(size[i])  ,1<=i<=31(表示第i个质数)  ,即一开始时,每一个二元组只有一个有效状态,(该二元组的其他所有状态一定没有当前状态值优),且这些状态表示的序列都为 size 个 p。该状态的权值即为pi^size[i]。因为要频繁询问当前最大权值和具有修改操作,我选择了可并堆。每一次,取出可并堆的堆顶元素,即为最大权值,表示我现在选择了它,可以找到它原来的那个序列,那么现在就要利用这个序列扩充新的序列。设size为这个二元组的size,p为这个二元组的p,那么相当于是要找到size个素数相乘,且最大素数为p。规定要用最小表示法,即这size个素数单调不下降组成一个序列,这样,我们就要枚举将其中的哪一个素数变小,它变化后不能破坏这个序列单调性,在枚举完后,会有一个新的权值(可以O(1)计算得),它可能已经在前面搜索中进堆了(举例来说,对于一个二元组(7,5),最初始为7*7*7*7*7,它接下来会搜索到序列(不是直接枚举到) 5*5*7*7*7 (记为f1)和3*7*7*7*7(记为f2) 显然f1会f2先出堆,f1 会搜到序列 3*5*7*7*7(记为f3),那么f3已经入堆了,之后如果f2出堆了,那么它也会枚举到f3,但此时f3已经不能入堆,否则将有一些数被算重),因此这里加一个哈希表来判。以此处理,每次出堆会算出当前最大值,且最多会多进入60个新的序列(其实远远达不到),那么时间复杂度就是多项式,O(k*logm)m为可并堆里面的元素个数。数组可能会吃紧,考虑到一个序列出堆后就没用了,可以写个回收站来回收空间以图再利用(可并堆里的元素也是如此)。这样虽然代码长了点,也能很好的解决这个问题了(ORZ%%%%%搜索+减枝过的),可能叙述有些难懂,代码还是很好理解的。(代码里用i表示第i个素数pi)


#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const LL N=650005,mod=1000003;
LL prime[200],num=0,size[200],m;
LL jilu[N],Add=0,He=0;
LL tmp[N]={0},top=0;
struct data{LL size,i;}P[2500];LL cnt=0;
LL map[N][62]; 
struct node{LL L,R,val,i;}tr[N];LL sign=0,root;
bool vst[200];
struct Haxi
{
	LL h[1000005],tot;
	struct qxx{LL to,next;}q[8000005];
	LL Ask(LL val)
	{
		LL i,x=val%mod;
		for(i=h[x];i;i=q[i].next)
			if(q[i].to==val)return 1;
		return -1;
	}
	LL Insert(LL val)
	{
		LL i,x=val%mod;
		tot++;
		q[tot].to=val;q[tot].next=h[x];h[x]=tot;
	}
}pq;
LL CNTNUM()
{
	if(Add)return jilu[Add--];
	return ++He;
}
LL CNTclean(LL x){jilu[++Add]=x;}
LL Numb()
{
	if(top)return tmp[top--];
	return ++sign;
}
void Clean(LL x)
{
	tr[x].L=tr[x].R=tr[x].i=tr[x].val=0;
	tmp[++top]=x;
}
void Build()
{
	LL i,j,x;
	for(i=2;i<=128;i++)
	{
		if(!vst[i])prime[++num]=i;
		for(j=1;j<=num;j++)
		{
			if(i*prime[j]>128)break;
			vst[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
    }
    LL n;
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=num;i++)
	{
		x=n;j=0;
		while(x>=prime[i]){x/=prime[i];j++;}
		size[i]=j;
	}
}
LL Ksm(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}
LL Merge(LL x,LL y)
{
	if(!x||!y)return x+y;
	if(tr[x].val1&&(map[i][j]>map[i][j-1]||j==1))
			{
				Toval=nowval;
				Toval=Toval/prime[map[i][j]]*prime[map[i][j]-1];
				if(pq.Ask(Toval)==-1)pq.Insert(Toval);
				else continue;
				cnt=CNTNUM();
				for(k=0;k<=map[i][0];k++)map[cnt][k]=map[i][k];
				map[cnt][j]--;
				x=Numb();
				tr[x].L=tr[x].R=0;tr[x].i=cnt;tr[x].val=Toval;
				root=Merge(root,x);
			}
		}
		CNTclean(i);
	}
}
int main()
{
	Build();
	Solve();
	return 0;
}


你可能感兴趣的:(OI)