题解:P10786 [NOI2024] 百万富翁
对于测试点
设
最后一个式子中
在第一个等号处,我们将
第二个等号处我们首先枚举
关于最后一个等号的成立,有引理:
直接递推是非常缓慢的,我们可以利用多线程的方式来进行计算。
附如下多线程加速循环的代码(
#include<vector>
#include<thread>
#include<cstring>
#include<fstream>
#include<functional>
using namespace std;const int N=1000005,M=15;
long long f[9][N];int g[9][N];
int main()
{
ofstream fout("D1T2.txt");
memset(f,0x3f,sizeof f),f[0][1]=0;
for(int k=1;k<=8;k++)
{
function<void(int)> func=[&](int i){
for (int p=1;p<=i;p++)
{
long long ord=f[k-1][p]+1ll*(p-i%p)*(i/p)*(i/p-1)/2+1ll*(i%p)*(i/p)*(i/p+1)/2;
if(f[k][i]>ord)f[k][i]=ord,g[k][i]=p;
}};
vector<thread> threads;
threads.reserve(M);
for(int i=0;i<M;i++)
{
threads.emplace_back([=,&func]()
{for(int t=i;t<N;t+=M)func(t);});
}
for(int i=0;i<M;i++)threads[i].join();
fout<<k<<" Complete"<<endl;
}
int pos=1000000;fout<<f[8][pos]<<endl;
for(int i=8;i;i--)fout<<g[i][pos]<<' ',pos=g[i][pos];
return 0;
}
该程序创建的文本 D1T2.txt 最后两行为:
1099944
500000 250000 125000 62496 20832 3472 183 1
代码略,按照上述分组方式构造即可。
程序实测本地运行时间
当然考场上不需要搞出最优,爆搜一下即可。
不过,我们也可以在本地提前跑一些小的数据并观察最优解,发现前几次都是直接两两分组。可以根据这个性质加速 dp。从
附表:
| 1 | 499999500000 |
| 2 | 93990141 |
| 3 | 8816504 |
| 4 | 3280700 |
| 5 | 2000713 |
| 6 | 1473837 |
| 7 | 1223204 |
| 8 | 1099944 |
| 9 | 1044824 |
| 10 | 1019840 |
| 11 | 1008877 |
| 12 | 1003893 |
| 13 | 1001726 |
| 14 | 1000746 |
| 15 | 1000288 |
| 16 | 1000109 |
| 17 | 1000041 |
| 18 | 1000011 |
| 19 | 1000002 |
| 20 | 999999 |