前言:
就在这篇博客发布的前几天,【LGR-052】洛谷9月月赛II(加赛)的第二题(链接:https://www.luogu.org/problemnew/show/P4889),这并不是一道很难的题,博主也很快想到了做法并敲出了以下这份代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <set>
typedef long long LL;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x;
}
const int MAXN=200005;
int n,m,a[MAXN];
std::multiset<int> s1,s2;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
LL ans=0;
for(int i=1;i<=n;i++){
ans+=s1.count(i-a[i]);
ans+=s2.count(i+a[i]);
ans+=s2.count(i-a[i]);
s1.insert(i-a[i]);
s2.insert(i+a[i]);
}
printf("%lld\n",ans);
return 0;
}
由于与本文讨论的主题无关,所以在此不阐述本题的详细做法。
不过可以注意到,博主使用了std::multiset
众所周知,multiset的.count()函数与set的.count()函数时间复杂度均为O(logn)(n表示集合元素的个数),所以这份代码的时间复杂度为O(nlogn)。
再次众所周知,以O(nlogn)的时间复杂度过n=200,000的数据点本应是很容易,但是博主只得到了30pts,有7个测试点TLE,并且开启O2优化后仍然TLE7个点。
<(OoO)?
后续:
在别人的提醒下,我将multiset换成了map,用一个映射来直接记录一个值出现的次数,敲出了以下代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <map>
typedef long long LL;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x;
}
const int MAXN=200005;
int n,m,a[MAXN];
std::map<int,int> f1,f2;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
LL ans=0;
for(int i=1;i<=n;i++){
if(f1.count(i-a[i])) ans+=f1[i-a[i]],f1[i-a[i]]=f1[i-a[i]]+1;
else f1[i-a[i]]=1;
if(f2.count(i-a[i])) ans+=f2[i-a[i]];
if(f2.count(i+a[i])) ans+=f2[i+a[i]],f2[i+a[i]]=f2[i+a[i]]+1;
else f2[i+a[i]]=1;
}
printf("%lld\n",ans);
return 0;
}
这份代码在不开启O2优化的情况获得了AC。
小结:
在能使用set或map的情况下,尽量避免使用multiset。
例如在这种统计一个数出现了几次这种问题中,multiset的常数我们无法把控,更无法预测它所带来的结果。
一句话,慎用multiset,慎用STL。
(可能还会更新......留个坑。)