ABC300 做题笔记
A
简单题,不讲。
B
数据范围很小,随便暴力。
感觉这种题放在这种位置上就没啥意思。码量不小,又不小清新,也不知道想考啥。
C
这题也没意思。直接枚举 #,然后往四角扩展直到不能扩展为止。如果扩展成功则计入答案。时间复杂度
D
这题时限 3s。
然后我赛时擦着时限过了,2.837 s。
现在还是最劣解第一页。
1. 我赛时的做法
这玩意其实很直觉,也特别暴力,什么优化都不带的那种。随便放缩一下就有
然后再放缩,于是
接下来就是最耗时的地方了。记
2. 优化
其实这个东西特别好优化,但是赛时我看它卡过去了就没管。
上面这个东西最耗时的地方有两点:
-
判断一个东西是不是质数。
-
数出
[l,r] 内有几个质数。
我们发现我们枚举的范围最多也只有
有了这两个简单优化,它跑得飞起,27ms 就过了。
for(int i=2;i*i*i*i*i<=n;i++)
{
if(isp[i]-isp[i-1])
{
int lft=n/(i*i);
for(int j=i+1;j*j*j<=lft;j++)
{
if(isp[j]-isp[j-1])
{
int rgt=lft/j;
int x=__builtin_sqrt(rgt);
cnt+=isp[x]-isp[j];
}
}
}
}
E
定义
于是
记忆化+递归求解即可。时间复杂度
int dp(int x)
{
if(vis.count(x)){return vis[x];}
if(x==1){return vis[x]=1;}
int ans=0;
for(int i=2;i<=6;i++)
{
if(x%i==0)
{
ans+=inv(6)*dp(x/i)%mod;
ans%=mod;
}
}
return vis[x]=((ans*6)%mod*inv(5)%mod);
}
F
这玩意很简单,就是注意细节。
我们考虑肯定存在一种答案满足左端点在
二分答案。然后枚举左端点,用前缀和和循环的性质判断结果是否满足条件。完结撒花。
好的,那么我为什么吃了 3 发罚时呢?
边界错误 1 次,拍出来的。
边界错误 2 次,拍出来的。
边界错误 3 次,拍出来的。
心态爆炸,终于过了。
bool check(int x)
{
for(int i=1;i<=n;i++)
{
if(n*m-(i-1)<x){continue;}//边界 1:如果剩下不足 x 个
if(i+x-1<=n)//边界二:如果根本没有循环
{
if(pre[x+i-1]-pre[i-1]<=k){return 1;}
continue;
}
int cur=x-(n-i+1);
int tmp=pre[n]-pre[i-1];
tmp+=pre[n]*(cur/n);
cur%=n;
tmp+=pre[cur];
if(tmp<=k){return 1;}
}
return 0;
}
G
我们发现
但是显然,这样搜就突出一个奇慢无比,最大数据 10 s 都跑不出来。交上去总共 T 了 25 个点。
这时我们就可以考虑 Meet in the middle 了。先对一半的质数搜一遍,搜出所有可以得到的结果;再对另一半的质数搜一遍,搜完之后假设结果是
但是如果就这么搜,还是会 TLE(在 Custom Test 中需要 7871 ms 才能跑的出来极限数据)。究其原因,还是因为后面那些大的质数其实根本跑不出来几个结果(只有 152097 个),而那些小的质数则有很多结果,不均衡。
如何解决?其实也很简单,把质数 random_shuffle 一下就可以做到相对的均衡了。这样极限数据也只需要跑 613ms。
lqy 神还有一种跑的很快的做法,供欣赏。
void init()
{
random_shuffle(pr,pr+cnt+1);
}
void dfs(int x,int cur)
{
if(x==cnt/2+1)
{
vis.pb(cur);return;
}
dfs(x+1,cur);
while(cur<=n/pr[x])
{
dfs(x+1,(cur*=pr[x]));
}
}
void dfs2(int x,int cur)
{
if(x==cnt+1)
{
ans+=upper_bound(vis.begin(),vis.end(),n/cur)-vis.begin();
return;
}
dfs2(x+1,cur);
while(cur<=n/pr[x])
{
dfs2(x+1,(cur*=pr[x]));
}
}
Ex
不会,咕。