2021 CCPC网络赛 HDU .7111 Remove(单调队列dp)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021 CCPC网络赛 HDU .7111 Remove(单调队列dp)相关的知识,希望对你有一定的参考价值。
给定一个素数集合 P P P,若一堆石子有 n n n个
你每次可以选择素数集合中的一个 p p p令 n = n − n % p n=n-n\\%p n=n−n%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=1∑Nf(n)∗23333N−n)%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=n−n%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+p−1]的所有 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网络赛) 堆 贪心