CF1989D Smithing Skill 题解
这篇题解是我在赛场上的真实思路,所以可能会比较冗长。但是相较于其他题解,读者可以在这篇题解中理解为什么会想到使用动态规划。我认为,授人以鱼不如授人以渔,理解为什么想到使用动态规划,比知道使用动态规划更重要。
首先,有一个比较显然的贪心。每次合成之后立即融掉,可以获得 \(2\) 点技能点,并失去材料 \(a_i-b_i\) 个。因此,我们把所有合成方式按照 \(a_i-b_i\) 升序排序,先合成消耗少的合成方式,再合成消耗多的合成方式。
考虑到需要 \(a_i\) 个材料才能合成,若 \(a_i-b_i\) 相等,我们采用 \(a_i\) 较小的合成方式,这样就可以合成更多次。不难发现如果一种合成方式 \(a_i-b_i\) 大于等于其他合成方式,并且 \(a_i\) 大于等于其他合成方式,这种合成方式一定不优,可以舍去。于是,现在的合成方式序列满足若 \(i\lt j\),则 \(a_i-b_i\lt a_j-b_j\) 且 \(a_i\gt a_j\)。对于某种数量 \(c\),最优的合成方式显然是满足 \(a_i\le c\) 的编号最小的位置。
每种材料互相独立,分别处理。首先考虑如何使用最优的一种合成方式进行合成。我们先留下 \(a_i\) 个材料,并对于剩下的材料进行合成,每次消耗 \(a_i-b_i\) 个,利用除法直接算出。这样就保证了至少有 \(a_i\) 个材料,一定可以合成。之后,我们求出合成后的余数,加到留下的 \(a_i\) 个中,然后暴力计算。显然,这个计算的次数不会太多,大概 \(0\sim1\) 次,可以看作常数。
根据数据范围,使用最优的一种合成方式进行合成后,剩余的材料数量不会超过 \(10^6\),考虑预处理。设 \(f[i]\) 表示剩余 \(i\) 个材料可以合成并融化的最多次数,我们发现,无论对于哪一个 \(i\),进行一部分操作之后,\(i\) 只会变小而不会变大。也就是说,如果从小到大枚举 \(i\),\(f[i]\) 的求解仅依赖于之前的状态,可以动态规划。
从小到大枚举 \(i\),我们发现,经过第一步的处理后,可以使用的最优的一种合成方式也在从编号大到编号小单调递减,可以直接维护。使用最优的一种合成方式进行合成,就可以直接将剩余的材料数量可以合成并融化的最多次数通过状态累加过来。于是,我们就在线性时间内预处理出了 \(f[i]\)。
最后,思维变得很明朗。如果 \(c\le10^6\),直接查表得到 \(f[c]\)。否则,使用最优的一种合成方式进行合成后,再查表累加进答案。
使用桶排序,时间复杂度为 \(O(n)\),非常优秀。
#include <bits/stdc++.h>
using namespace std;
long long t,n,m,c,a[1500000],b[1500000],s[1500000],q[1500000],p[1500000],f[1500000],cnt=0,now=0,ans=0;
inline long long read()
{
long long x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main()
{
for(int i=0;i<=1000000;i++)s[i]=1e9;
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
for(int i=1;i<=n;i++)s[a[i]-b[i]]=min(s[a[i]-b[i]],a[i]);
cnt=0,q[cnt]=1e9,p[cnt]=0;
for(int i=1;i<=1000000;i++)
if(s[i]<q[cnt])q[++cnt]=s[i],p[cnt]=i;
now=cnt;
for(int i=1;i<=1000000;i++)
{
while(q[now]<=i&&now>0)now--;
if(now<cnt)now++;
else continue;
c=i,f[i]+=(c-q[now])/p[now];
c=(c-q[now])%p[now]+q[now];
while(c>=q[now])c-=p[now],f[i]++;
f[i]+=f[c];
}
for(int i=1;i<=m;i++)
{
c=read();
if(c<q[1])
{
ans+=f[c];
continue;
}
ans+=(c-q[1])/p[1];
c=(c-q[1])%p[1]+q[1];
while(c>=q[1])c-=p[1],ans++;
ans+=f[c];
}
printf("%lld\n",ans*2);
return 0;
}

浙公网安备 33010602011771号