[loj2978]杜老师

假设所有素数从小到大依次为$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-l\ge 6661$时,线性基一定会满,即线性基内数的个数即$[l,r]$中所有出现的素因子个数,这个可以枚举素数来计算

最终,复杂度即$o(t(\pi(r)+\frac{6661\pi(\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\n",mi[ans]);
62     }
63     return 0;
64 } 
View Code

 

posted @ 2021-03-28 14:22  PYWBKTDA  阅读(85)  评论(0编辑  收藏  举报