HDU6753&DTOJ4963 2020 Multi-University Training Contest 1 cookies(看星)

题目

原题

题目描述

Elsa’s elder brother Eric has got $n$ cookies in his icebox and each cookie has a special number written on it. Let’s denote the number written on the $i^{th}$ cookie by $f_i$. $f_i$ is defined as follows
f_i is defined as this
Here, $divmed(x)$ states the median value of divisors of $x$. Let $x$ has $k$ divisors, then $divmed(x)$ is $\left\lceil\frac{k}{2}\right\rceil^{th}$ smallest divisor. For example, $divmed(100)=10$, $divmed(10)=2$, $divmed(1)=1$.
One day, Eric opened the icebox and recognized that some of his cookies are missing. His sister Elsa had eaten some of them.
On the first day, she ate cookies that have multiples of $p_1$ as indexes and then, re-indexed them starting from $1$ according to their original order. In the same way, she ate cookies with multiples of $p_i$ as indexes on the $i^{th}$ day and re-indexed them. If there were less than $p_i$ cookies left, she ate none of them. Elsa continued to do so for m days.

As Elsa is keen on math and Eric didn’t want to blame his sister, Eric asked her to find out the number written on the $k^{th}$ of remaining cookies.

输入格式

The first line of the input contains an integer$T(1\leqslant T\leqslant 60)$, denoting the number of test cases. The first line of each test case contains $3$ positive integers $n(1\leqslant n\leqslant 10^{10}), m(1\leqslant m\leqslant 10^5)$and$k(1\leqslant k\leqslant 10^{10})$. The next line contains m integers$p_1,p_2,\cdots \cdots,p_m (1\leqslant p_i\leqslant 10^{10})$.

输出格式

For each test case, output one line containing the answer. If the kth cookie doesn’t exist, then output $-1$ in a single line.

样例

样例输入

1
2
3
4
5
2
25 5 9
3 5 8 12 10
25 5 2
3 5 8 12 10

样例输出

1
2
31
2

题解

在OI界,有一句名言:“暴力出奇迹,打表出省一”
如果你认为这句话不可能,那么你就错了
你可以点开HDU上的statistic,看看大家的代码长度(已按代码长度从小到大排序):
HDU中的statistic也就是说,最短的代码也有22.5K!!!
这不是打表是什么?
接下来,我们就来看看这道打表题
显然,我们可以用二分或计算,找到第$k$个饼干初始的位置$K$,那么问题就在于求$\sum\limits_{i=1}^K divmed(i)$的值了
显然,这个东西计算效率很低,因为你肯定需要枚举$1\sim \sqrt{K}$之间的所有数,然后计算,效率很低(事实证明很低,我也算不出来)
所以我们可以考虑分段打表!
取$2500000$为一段,计算$\sum\limits_{i=t+1}^{t+2500000}divmed(i)$($2500000|t$),然后把求出来的$4000$个值复制到主程序中,就可以了
计算时用类似于挨氏筛质数的方法就可以了
打表代码如下(写题解的时候打的,可能有问题,自己改一下):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const int D=2500000;
long long l,r,a[4010],d[D+10];
int main()
{
printf("const long long a[4010]={0,");
for(int t=1;t<=4000;++t){
a[t]=a[t-1],l=(t-1)*D+1,r=t*D;
for(long long i=1;i<=100000;i++){
if(i*i>r) break;
for(long long L=((l-1)/i)*i+1,j=(r/i)*i;i*i<=j&&L<=j;j-=i) d[j-l]=i;
}
for(long long i=l;i<=r;i++) a[t]+=d[i-l];
printf("%lld,",a[t]);
}
printf("};");
}

我自己运行这个程序花了$24$分钟
然后剩下的内容就类似于分块了,如果有剩余的,暴力计算即可
最后还有一个要注意的点,就是为了节省时间,对剩余的部分,如果长度不到$1250000$,就加上正着算的结果,如果超过$1250000$,就减去倒着算的结果(这个优化在HDU上可加可不加,但是在DTOJ上不加就会挂,就像我一样,调了一个中午,连饭都没吃……abcde真是太毒瘤啦!
完整代码如下(打表的内容自行填充):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<cstdio>
const long long D=2500000,a[]={0,876804130,2440016214,...,190808468780767};
long long n,m,k,ans,flag,l,r,p[100010],d[D+10];
template<class T>inline void read(T &x)
{
x=0;int f=0;char ch=getchar();
while(ch<'0'||ch>'9') {f|=(ch=='-');ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return;
}
int main()
{
int T;
read(T);
while(T--){
read(n),read(m),read(k),flag=0;
for(register int i=1;i<=m;i++) read(p[i]);
for(register int i=m;i;i--){
if(p[i]==1){k=1;break;}
else if(k>n){flag=1;break;}
else k+=(k-1)/(p[i]-1);
}
if(flag||k>n){printf("-1\n");continue;}
ans=a[k/D];
if(k%D){
if(k%D<=D/2){
l=(k/D)*D+1,r=k;
for(register long long i=1;i<=100000;i++){
if(i*i>r) break;
for(register long long L=((l-1)/i)*i+1,j=(r/i)*i;i*i<=j&&L<=j;j-=i) d[j-l]=i;
}
for(long long i=l;i<=r;i++) ans+=d[i-l];
}
else{
ans=a[k/D+1],l=k+1,r=(k/D+1)*D;
for(register long long i=1;i<=100000;i++){
if(i*i>r) break;
for(register long long L=((l-1)/i)*i+1,j=(r/i)*i;i*i<=j&&L<=j;j-=i) d[j-l]=i;
}
for(long long i=l;i<=r;i++) ans-=d[i-l];
}
}
printf("%lld\n",ans);
}
}