0%

CF933D A Creative Cutout 题解

$\text{SYT}$ 学长在模拟赛中出了这道题,并把数据范围加到了 $n\leq 10^{14}$

学长的式子相似,仍记 $a=x^2+y^2$ ,点 $(x,y)$ 最早被半径为 $\sqrt a$ 的第 $a$ 个圆包含

枚举每个点被包含圆的编号 $\times$ 在 $n$ 个圆中的出现次数,即能推出上面的式子

但由于插值法常数过大,考虑继续将式子化简

将 $a=x^2+y^2$ 代入并以 $y$ 为主元,能得到:

该式的结果是点 $(x,y)$ 对答案的贡献,但还可以更加简化:在 $O(\sqrt n)$ 的时间内枚举正半轴上的每个 $x$ ,有 $max_y=\sqrt{n-x^2}$

对于每个 $x$ ,预处理所有的 $y$ 的六次方,四次方,二次方之和,$O(1)$ 求满足 $x^2+y^2\leq n$ 的所有 $y$ ,即,所有满足条件的 $(x,*)$ 点对答案的贡献和

看个图理解一下

以 $x=1$ 为例,红线部分就是所求的和

为了运算方便,实际上代码中求的是:

也就是

$y$ 的部分 $\times 2$ 是为了加上负半轴的贡献,常数项 $\times (2\times max_y+1)$ 中,$+1$ 是绿色点的部分,即在 $x$ 轴上时的情况

最终统计答案时,再用:

1
ans+=(!x) ? added : 2*added;

来处理 $x$ 的正负半轴情况以计算整个圆的情况

代码复杂度为 $O(\sqrt{n})$ ,其瓶颈在于取模(确信),若取模过于频繁就会从 $900ms$ 掉到 $4s$

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
#include <bits/stdc++.h>
#define rei register int
#define ll long long
using namespace std;

const int mod=1e9+7;
const int inv2=500000004,inv3=333333336,inv6=166666668;
ll n,mod_n,ans;

int main(){
scanf("%lld",&n);
mod_n=n%mod;
for(ll x=(ll) sqrt(n+0.1),i=1,sum2=0,sum4=0,sum6=0;~x;--x) {
ll t=sqrt(n-x*x),x2=x*x%mod;
for(;i<=t;++i){
ll pow_2=i*i%mod;
ll pow_4=pow_2*pow_2%mod;
sum2=(sum2+pow_2)%mod;
sum4=(sum4+pow_4)%mod;
sum6=(sum6+pow_2*pow_4)%mod;
}
ll added=((ll)2*inv3*sum6%mod
+(2*x2-2-mod_n+2*mod)%mod *sum4%mod
+( (1+mod_n)*((mod-2)*x%mod*x%mod+1)%mod + 2*x2*x2%mod-2*x2+inv3+2*mod )%mod *sum2%mod
+ (inv2*(1+mod_n)%mod*(mod_n+x2)%mod*(mod_n-x2+mod+1)%mod
-inv6*mod_n%mod*(mod_n+1)%mod*(2*mod_n+1)%mod
+inv6*(x2-1)%mod*x2%mod*(2*x2-1)%mod
+mod
)%mod*(2*t+1)%mod
)%mod;
ans+=(!x) ? added : 2*added;
}
printf("%lld\n",ans%mod);
getchar(),getchar();
return 0;
}