假设所有素数从小到大依次为$p_{1},p_{2},...,p_{k}$,我们将$x$转换为一个$k$位的二进制数,其中从低到高第$i$位为1当且仅当其$p_{i}$的幂次为奇数
不难发现以下两个性质:
1.假设$x$和$y$转换得到的二进制数分别为$x'$和$y'$,则$xy$转换后二进制数为$x'oplus y'$
2.$x$为完全平方数当且仅当$x$转换得到的二进制为0
由此,实际上也就是统计$[l,r]$这些数所转换的二进制数有多少个子集结果为0
考虑线性基,当构造得到线性基后,根据线性基有以下性质:对于线性基外任取若干个数,线性基内恰存在一种取法使得最终异或结果为0
首先,由于线性基外的每一个数都能与线性基内的若干个数异或为0,将所有数异或起来也就得到了一种方案,同时若有两种方案,即线性基内存在若干个数异或为0,也矛盾
由此,我们得到了方案数就是$2^{r-l+1-线性基内数的个数}$
暴力求线性基复杂度是$o(frac{r-l+1}{omega}pi(r)^{2})$(其中$pi(r)$表示小于等于$r$的素数个数),需要优化:
由于大于$sqrt{r}$的素因子至多只有1个,因此可以手动模拟这一次操作,具体来说遍历时在第一次出现某个大素数(大于$sqrt{r}$的素数)时记录该数,并令之后出现含有此因子的都异或该数
这样做的复杂度降为$o(frac{r-l+1}{omega}pi(sqrt{r})^{2})$,但还是无法通过
进一步的,暴力验证可以发现在$r-lge 6661$时,线性基一定会满,即线性基内数的个数即$[l,r]$中所有出现的素因子个数,这个可以枚举素数来计算
最终,复杂度即$o(t(pi(r)+frac{6661pi(sqrt{r})^{2}}{omega}))$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 10000005 4 #define mod 998244353 5 #define L 6661 6 #define P 446 7 bitset<P>g,bt[L],f[P]; 8 int t,l,r,mi[N],vis[N],p[N],lst[N]; 9 int main(){ 10 mi[0]=1; 11 for(int i=1;i<N;i++)mi[i]=2*mi[i-1]%mod; 12 vis[1]=1; 13 for(int i=2;i<N;i++){ 14 if (!vis[i])p[++p[0]]=i; 15 for(int j=1;(j<=p[0])&&(i*p[j]<N);j++){ 16 vis[i*p[j]]=1; 17 if (i%p[j]==0)break; 18 } 19 } 20 scanf("%d",&t); 21 while (t--){ 22 scanf("%d%d",&l,&r); 23 int ans=r-l+1; 24 if (r-l>=L){ 25 for(int i=1;p[i]<=r;i++) 26 if (r/p[i]>(l-1)/p[i])ans--; 27 } 28 else{ 29 for(int i=0;i<P;i++)f[i].reset(); 30 for(int i=l;i<=r;i++){ 31 int k=i; 32 for(int j=1;j<=P;j++){ 33 int tot=0; 34 while (k%p[j]==0){ 35 tot^=1; 36 k/=p[j]; 37 } 38 bt[i-l][j-1]=tot; 39 } 40 g=bt[i-l]; 41 if (k>1){ 42 if ((lst[k]<l)||(lst[k]>=i))lst[k]=0; 43 if (!lst[k]){ 44 lst[k]=i; 45 ans--; 46 } 47 g^=bt[lst[k]-l]; 48 } 49 for(int j=0;j<P;j++){ 50 if (!g.any())break; 51 if (g[j]){ 52 if (!f[j].any()){ 53 f[j]=g; 54 ans--; 55 } 56 g^=f[j]; 57 } 58 } 59 } 60 } 61 printf("%d ",mi[ans]); 62 } 63 return 0; 64 }