题解:P13423 [COCI 2019/2020 #4] Spiderman

· · 题解

苯蒻的第一篇题解(轻喷)

Part 1 读题

题意转化:有n个数,对于每个a_i(1\le i\le n),求满足a_i\mod a_j = kj的个数。

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; } ``` 为防止作弊,空白部分需要自己填。