巧克力
杜老师
LOJ
我们知道完全平方数的质因子个数都是偶数。
假如我们把每个数用一个记录它的质因子的个数奇偶性的bitset来表示,那么这题就变成了问\([L,R]\)内有多少个子集满足它们的bitset异或起来为\(0\)。
假如我们把这些元素一个个插入线性基,那么答案就是\(2^{R-L+1-n}\)。(\(n\)为线性基的元素个数)
那么我们考虑如何快速求出这个\(n\)。
找题解可以发现,如果\(R-L>6660\)(这里写的是\(6000\)),那么这个区间内的每一个出现的质数的线性基都会被插入线性基,那么我们可以直接扫一遍计算。
否则我们从\([L,R]\)枚举每个元素,计算出它们所对应的bitset,然后插入线性基即可。
注意到对于在\([L,R]\)范围内的所有数,\(>\sqrt R\)的质因子最多只有一个。
所以我们可以只要筛出\(\sqrt R\)以内的质因子,剩下的那个单独用数组判断一下之前有没有出现过这个质因数,如果没有那么这也是线性基的一个元素(但是我们不用插入而是记录在这里),如果有那么直接异或上记录的那个元素然后插入线性基。这个方法的正确性是显然的。
复杂度大概\(O(6000T\sqrt{10000000})\),不过跑起来飞快。
#include<bits/stdc++.h>
using namespace std;
const int N=500,S=6007,M=10000007,P=998244353;
int read(){int x;scanf("%d",&x);return x;}
int mul(int a,int b){return 1ll*a*b%P;}
int power(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int id[M],pr[700007],tot,L,R,n,vis[M],stk[S],top;
bitset<N>a[N],t[S];
void init(int lim)
{
for(int i=2,j;i<=lim;++i)
{
if(!id[i]) pr[++tot]=i,id[i]=tot;
for(j=1;j<=tot&&i*pr[j]<=lim;++j)
{
id[i*pr[j]]=j;
if(!(i%pr[j])) break;
}
}
}
void insert(bitset<N>x)
{
for(int i=499;~i;--i)
if(x[i])
{
if(a[i][i]) x^=a[i];
else {a[i]=x,++n;break;}
}
}
void solve()
{
L=read(),R=read(),n=top=0;
if(R-L>=6000)
{
for(int i=1;pr[i]<=R;++i) if(R/pr[i]>(L-1)/pr[i]) ++n;
return (void)(printf("%d\n",power(2,R-L+1-n)));
}
for(int i=0;i<500;++i) a[i].reset();
for(int i=L,x,p;i<=R;++i)
{
t[p=i-L+1].reset();
for(x=i;x^1&&id[x]<500;x/=pr[id[x]]) t[p].flip(id[x]);
if(x==1) insert(t[p]);
else if(!vis[x]) vis[x]=p,++n,stk[++top]=x;
else t[p]^=t[vis[x]],insert(t[p]);
}
for(int i=1;i<=top;++i) vis[stk[i]]=0;
printf("%d\n",power(2,R-L+1-n));
}
int main()
{
init(10000000);
for(int T=read();T;--T) solve();
}