2021 CCPC网络赛 HDU .7111 Remove(单调队列dp)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021 CCPC网络赛 HDU .7111 Remove(单调队列dp)相关的知识,希望对你有一定的参考价值。

LINK

给定一个素数集合 P P P,若一堆石子有 n n n

你每次可以选择素数集合中的一个 p p p n = n − n % p n=n-n\\%p n=nn%p

最后你要让 n n n变为 0 0 0,或者无解,我们定义 f ( i ) f(i) f(i)表示把一堆石子 i i i变为零的最小操作次数

( ∑ n = 1 N f ( n ) ∗ 2333 3 N − n ) % 2 64 (\\sum\\limits_{n=1}^N f(n)*23333^{N-n})\\% 2^{64} (n=1Nf(n)23333Nn)%264

特别的,如果 n n n无解令 f ( n ) = 0 f(n)=0 f(n)=0


一、

m x mx mx表示素数集合 P P P中最大的素数

m x > n mx>n mx>n那么用 m x mx mx操作一次 n n n就变成 0 0 0

否则,我们需要让 n n n操作尽量少的步骤使得小于 m x mx mx,再用 m x mx mx操作一次变为 0 0 0

那么可以暴力枚举每个素数 p p p转移 f [ i ] f[i] f[i]

设当前使用素数 p p p操作, n n n变为 n 1 = n − n % p n_1=n-n\\%p n1=nn%p

f [ n ] = max ⁡ { f [ n 1 ] + 1 } f[n]=\\max\\{f[n_1]+1\\} f[n]=max{f[n1]+1}


二、

然而枚举 p p p的复杂度太高,注意到 n 1 n_1 n1 p p p的倍数

若存在 i % p = = 0 i\\%p==0 i%p==0,可以用 f [ i ] + 1 f[i]+1 f[i]+1去更新 j ∈ [ i + 1 , i + p − 1 ] j\\in[i+1,i+p-1] j[i+1,i+p1]的所有 f [ j ] f[j] f[j]

而且发现 f [ i ] + 1 f[i]+1 f[i]+1固定和 p p p无关

于是我们从素数集合 P P P中找到最大的 p p p满足 i % p = = 0 i\\%p==0 i%p==0

这样可更新的区间范围覆盖了选择其他素数的更新范围,只需要更新一次

区间更新可以用线段树,但是仍然太慢

我们注意到根据上面的转移, f f f数组是非递减的,于是可以用一个单调队列优化掉

#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
const int maxn = 2e6+10; 
int n,P,top,vis[maxn],sta[maxn],tim[maxn],p[maxn],ok[maxn],f[maxn],prime[maxn],mi[maxn],mxzhi[maxn];
vector<int>vec[maxn];
ULL base[maxn];
void init()
{
	int sum = 0;
	base[0] = 1; base[1] = 23333;
	for(int i=2;i<=2000000;i++)
	{
		base[i] = base[i-1]*23333;
		if( !vis[i] )	prime[++top] = i, mi[i] = i;
		for(int j=1;j<=top && prime[j]*i<=2000000; j++)
		{
			vis[prime[j]*i] = 1; mi[prime[j]*i] = prime[j];
			if( i%prime[j]==0 )	break;
		}
	}
}
int main()
{
	init();
	int t; cin >> t;
	while( t-- )
	{
		scanf("%d%d",&n,&P);
		int mxp = 0;
		for(int i=1;i<=P;i++)
			scanf("%d",&p[i] ), ok[p[i]] = 1, mxp = max( mxp,p[i] );
		int head = 1, tail = 0;
		for(int i=1;i<=n;i++)
		{
			if( i==1 )	mxzhi[i] = -1;
			else	mxzhi[i] = mxzhi[ i/mi[i] ];
			if( ok[ mi[i] ] )	mxzhi[i] = max( mxzhi[i],mi[i] );
			f[i] = 0;
			while( head<=tail && tim[head]<i )	head++;
			if( head<=tail )	f[i] = sta[head]; 
			if( i<mxp )	f[i] = 1;
			if( f[i]!=0 && mxzhi[i]!=-1 )//使用f[i]+1更新[i+1,i+mxzhi-1]
				sta[++tail] = f[i]+1, tim[tail] = i+mxzhi[i]-1;
		}
		ULL ans = 0;
		for(int i=1;i<=n;i++)	ans += base[n-i]*f[i];
		cout << ans << endl;
		for(int i=1;i<=P;i++)	ok[p[i]] = 0;
	}
}

以上是关于2021 CCPC网络赛 HDU .7111 Remove(单调队列dp)的主要内容,如果未能解决你的问题,请参考以下文章

hdu6153 A Secret CCPC网络赛 51nod 1277 KMP

HDU6706 CCPC 2019网络赛 huntian oy 推式子+杜教筛

HDU 6438 Buy and Resell (CCPC网络赛) 堆 贪心

2019CCPC网络赛 HDU 6702——找规律

hdu 6152 : Friend-Graph (2017 CCPC网络赛 1003)

CCPC 2019 网络赛 HDU huntian oy (杜教筛)