题解:P13423 [COCI 2019/2020 #4] Spiderman
mountainwithwater
·
·
题解
苯蒻的第一篇题解(轻喷)
Part 1 读题
题意转化:有n个数,对于每个a_i(1\le i\le n),求满足a_i\mod a_j = k的j的个数。
Part 2 分析时间复杂度
需要$O(n),O(nlogn),
O(k),O(klogk)$的算法。
似乎$O(n)和O(k)$不太可能,于是我们将目光投向$O(nlogn)和O(klogk)$。
### Part 3 设计算法
根据带余除法,$a_i\mod a_j=k$可以转化成$a_i=m*a_j+k$。
于是我们就可以枚举$a_j$和$m$,那么$a_i$也就唯一确定了。
预处理每个$x$可以跳到几栋楼,到时候求$a_i$的答案,只需调用即可
时间复杂度呢?
有双重循环,看上去是$\max(a_i)^2的$。
不知道大家还记不记得埃氏筛的复杂度。
$$
\frac{N}{1}+\frac{N}{2}+\frac{N}{3}+\cdots+\frac{N}{N-1} \approx N \ln N
$$
没错,这就是我们要的$O(klogk)!
### Part 4 UNAC code
```cpp
#include <bits/stdc++.h>
using namespace std;
int n,k,h,cnt[1000100],a[300010],ans[1000010];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
cnt[a[i]]++;
h=max(a[i],h);
}
for(int i=1+k;i<=h;i++){
for(int j=0;j*i+k<=h;j++){
int x=j*i+k;
if(x==i) continue;
ans[x]+=cnt[i];
}
}
for(int i=1;i<=n;i++){
cout<<ans[a[i]]<<" ";
}
return 0;
}
```
然鹅,这份代码交上去只有63。
### Part 5 Hack
试试这组数据。
```
2 0
6 6
```
你会发现,程序会把$a_i$自身算进答案里。
于是,在$k=0$时需要将所有答案$-1$。
### Part 6 AC code
```cpp
#include <bits/stdc++.h>
using namespace std;
int n,k,h,cnt[1000100],a[300010],ans[__1__];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
__2__;
h=max(a[i],h);
}
for(int i=__3__;i<=h;i++){
for(int j=0;j*i+k<=h;j++){
int x=__4__;
ans[x]+=cnt[i];
}
}
for(int i=1;i<=n;i++){
cout<<(__5__?ans[a[i]]-1:ans[a[i]])<<" ";
}
return 0;
}
```
为防止作弊,空白部分需要自己填。