description

\(T\)次询问,每次问\(L,L+1...R\)有多少种子集满足子集中乘积为完全平方数。

solution

50pt

首先双倍经验
通常的思路是:平方数即每个质因子指数为偶
奇偶性有关问题用异或!
用二进制(位数大,这里用bitset)每个质因子代表一位,表示该质因子指数的奇偶性。
就相当于问所有数对应bitset异或起来为0的方案数。
线性基中的个数为\(c\),方案数为(算空集)\(2^{n-c}\)
首先不在线性基里面的个数(\(n-c\))为自由元个数,也可以理解为线性基外的无论怎么选,都能用线性基里的唯一构造出异或和为\(0\)。(线性基外的选了,再选线性基里面异或和等于它的几个)。
因此完成了问题的转化。
回到这道题,暴力的50pt可以过了。一次插入复杂度\(\pi^2(n)\)
当然可以优化,到\(\pi^2(\sqrt{n})\)
每个数质因数分解最多只会有一个\(>\sqrt{n}\)的质因子。
bitset和线性基只需要维护\(<=\sqrt{n}\)的质数即可。
对于\(>\sqrt{n}\)位的线性基,开一个unordered_map<质因子,对应bitset>。
挺妙的,感觉自己的大脑根本没有创造力!

100pt

50pt到100pt的桥梁只是一个结论:质因子\(n\)\(r-l+1>\sqrt{n}\)\([l,r]\)中存在数含有质因子\(n\)\(n\)一定会被加入线性基
证明?
这样根号分治一下
1.\(len<=\sqrt{10^7}\),暴力线性基
2.\(len>\sqrt{10^7}\),枚举每个质数\(p\)判断\([l,r]\)是否存在它的倍数(\(\left\lfloor\dfrac{r}{p}\right\rfloor \ne \left\lfloor\dfrac{l-1}{p}\right\rfloor\))

贺的 code:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int SN=455;
typedef bitset<SN> bit;
typedef long long ll;
const int Sq=3200;
const int mod=998244353;
const int N=1e7+1;
bool is_p[N];
int p0[N],p[N],ptot,tot0;
void _xxs() {
	is_p[1]=1;
	for(int i=2;i<N;i++) {
		if(!is_p[i]) {
			if(i<=Sq)p0[++tot0]=i;
			p[++ptot]=i;
		}
		for(int j=1,x;j<=ptot&&(x=p[j]*i)<N;j++) {
			is_p[x]=1;
			if(i%p[j]==0)break;
		}
	}
//	printf("ptot = %d  tot0 = %d\n",ptot,tot0);
//	for(int i=1;i<=10;i++)printf("%d ",p0[i]);puts(""); 
}
int S;
bit a[SN];		//线性基 
unordered_map<int,bit> mp;
void Insert(int x) {
	bit v;
	for(int i=1;i<=tot0;i++) {
		if(x%p0[i])continue;
		int w=0;
		while(x%p0[i]==0) {x/=p0[i];w^=1;}
		if(w)v[i]=1;
	}
	if(x>1) {
		if(!mp.count(x)) {mp[x]=v;S++;return;}
		else {v^=mp[x];}
	}
	for(int i=tot0;i>=1;i--) {
		if(!v[i])continue;
		if(!a[i].any()) {a[i]=v;S++;return;}
		v^=a[i];
	}
}
ll ksm(ll x,ll y) {ll mul=1;for(;y;y>>=1,x=x*x%mod)if(y&1)mul=mul*x%mod;return mul;} 
void Clear() {S=0;for(int i=tot0;i>=1;i--)a[i].reset();mp.clear();}
int main() {
	_xxs();
	int T;scanf("%d",&T);
	while(T--) {
		Clear();
		int l,r;scanf("%d%d",&l,&r);
		int len=r-l+1;
		if(len<=7000) {
			for(int i=l;i<=r;i++) Insert(i);
		}
		else {
			for(int i=1;i<=ptot&&p[i]<=r;i++) {
				if(r/p[i]!=(l-1)/p[i]) {S++;}
			}
		}
		printf("%lld\n",ksm(2,len-S));
	}
	return 0;
}