【洛谷7451】[THUSCH2017] 杜老师(线性基)
- 求\(L\sim R\)中能选出多少不同的子集,满足子集中所有数的乘积是一个完全平方数。
- 数据组数\(\le100\),\(L\le R\le10^7\)
完全平方数与线性基
可以先看看这道题:【CF895C】Square Subsets。
根据那道题的套路我们就知道了,一个数是完全平方数,等价于它的每个质因子的次数都是偶数。
如果我们可以把每个数质因数分解了,由于一个数的贡献只需要考虑它的每种质因数次数的奇偶性,所以就可以被表示为一个二进制数。
这样一来,一个非空子集的乘积为完全平方数,当且仅当其中数对应的二进制数异或和为\(0\)。
要判断有多少数异或和为\(0\)就很简单,只要把所有数插入线性基,那么线性基外的每个数都可以表示为线性基中若干数的异或和,进而线性基外的每个非空集合都可以表示为线性基中若干数的异或和。
所以假设线性基外有\(t\)个数,则答案就是\(2^t-1\)。
但是对于此题,值域为\(10^7\),不可能把每个数暴力插入线性基,一定需要一些优化。
根号分治
令\(n\)为值域上界\(10^7\)。
显然,一个大于\(\sqrt n\)的质因子最多只可能存在一个。
因此,我们最初的线性基只需要记录\(\sqrt n\)以内的所有质因子(大约\(450\)个)。
插入一个数时如果它存在一个新的大因子就专门为它设一个特殊位,把它扔那里。否则,把它和它的大因子对应的特殊位异或一下就把大因子消掉了,且根据我们的操作它不可能存在其他特殊位为\(1\),所以接下来就可以直接把它插入普通的线性基了。
但由于\(l\le r\le10^7\),即便这样似乎也完全不可能过。
但随便想想就知道,这个线性基是很容易被塞满的,实际上可以认为当\(r-l+1\ge2\sqrt n\)时,所有出现过的质因子都将存在于线性基中,此时只要枚举所有质数判断一下是否在区间内即可。
代码:\(O(T\sqrt n\frac{450^2}{32})\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10000000
#define SN 3200
#define T 450
#define X 998244353
using namespace std;
int L,R,pw[N+5],St[N+5];bitset<T+5> s,S[T+5],G[2*SN+5];
int Pt,P[N+5],id[N+5];I void Sieve() {for(RI i=2,j;i<=N;++i)//线性筛求质数
for(!P[i]&&(P[++Pt]=i),j=1;j<=Pt&&i*P[j]<=N;++j) if(P[i*P[j]]=1,!(i%P[j])) break;}
I bool Ins(bitset<T+5> x) {for(RI i=1;i<=T;++i)//插入线性基
if(x.test(i)) {if(S[i].any()) x^=S[i];else return S[i]=x,1;}return 0;}
int main()
{
RI i,j;for(Sieve(),i=1;i<=T;++i) id[P[i]]=i;//给小质数标号
RI Tt,x,y,t,TP;for(pw[0]=i=1;i<=N;++i) pw[i]=2LL*pw[i-1]%X;scanf("%d",&Tt);W(Tt--)
{
if(scanf("%d%d",&L,&R),R-L+1>2*SN)//如果区间长度超过2sqrt(n)
{for(t=R-L+1,i=1;i<=Pt&&P[i]<=R;++i) ((L-1)/P[i])^(R/P[i])&&--t;printf("%d\n",pw[t]);continue;}//枚举每个质因子判断是否出现过
for(i=1;i<=T;++i) S[i].reset();for(t=TP=0,i=L;i<=R;++i)
{
for(s.reset(),x=i,j=1;j<=Pt&&P[j]*P[j]<=x;++j) {y=0;W(!(x%P[j])) x/=P[j],y^=1;y&&(s.set(j),0);}//质因数分解
x^1&&x<=P[T]&&(s.set(id[x]),x=1);//如果剩余的是小质数
if(x^1) {if(!id[x]) {St[id[x]=++TP]=x,G[TP]=s;continue;}s^=G[id[x]];}!Ins(s)&&++t;//如果剩余大质数和特殊位搞一搞,然后插入普通线性基
}
printf("%d\n",pw[t]);W(TP) id[St[TP]]=0,G[TP--].reset();
}return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒