洛谷 P7451 - [THUSCH2017] 杜老师(线性基+根分+结论题)

题面传送门

看到乘积为平方数我们可以很自然地想到这道题,具体来说,我们对 \(1\sim 10^7\) 中所有质因子标号 \(1,2,\cdots,\pi(10^7)\),对于 \(x\in[l,r]\) 中的数我们采用试除法对 \(x\) 分解质因数并求出每个质因子次数的奇偶性。我们建一个每个元素都是 \(0/1\) 的向量 \(b\),对于质因子 \(p_i\),如果 \(p_i\)\(x\) 的质因数分解式中次数为奇数,那么 \(b\) 的第 \(i\) 位为 \(1\),否则 \(b\) 的第 \(i\) 位为 \(1\),那么一个集合 \(S\) 中所有数的乘积为完全平方数当且仅当 \(S\) 中所有数表示的向量在 \(\bmod 2\) 意义下的和为零向量。这显然可以通过线性基维护,01 向量的异或运算可用 bitset 优化。记线性基大小为 \(x\),那么答案为 \(2^{r-l+1-x}\)。复杂度 \(\dfrac{n\pi^2(n)}{\omega}\approx 10^{18}\),无法通过。

考虑优化,注意到对于所有 \(>\sqrt{10^7}\) 的因子,它在所有数中至多出现一次。我们考虑只对 \(1\sim\sqrt{10^7}\) 中的质数编号——这样的质数的个数大约在 \(450\) 左右。记 \(mxp_i\)\(i\) 最大的质因子。我们对每个 \(>\sqrt{n}\) 的质因子 \(p\) 额外建一个 bitset 维护最大质因子为 \(p\) 的数的质因子情况,当我们插入某个数 \(x\) 的时候,若 \(mxp_x>\sqrt{10^7}\),我们就检验 \(mxp_x\) 对应的 bitset 是否为空,如果是那就令该 bitset\(x\) 表示的向量,否则就用 \(x\) 表示的向量异或上 \(mxp_x\) 对应的 bitset 再插入线性基,不难发现该操作与线性基实际上是等价的,只不过将线性基的规模由 \(\pi(10^7)\approx 10^6\) 缩小到了 \(\pi(\sqrt{10^7})\approx 450\)。不过复杂度还是高达 \(\dfrac{n\pi^2(\sqrt{n})}{\omega}\approx 10^{10}\),还是无法通过。

下一步就非常考验观察能力了,注意到当区间长度大于某个临界值的时候,线性基是非常容易塞满的,在这种情况下所有在 \([l,r]\) 中出现过的质因子都会被加入线性基中,因此我们只需统计 \([l,r]\) 中有多少个质因子即可,具体来说枚举所有 \([1,r]\) 的质因子 \(p\) 并检验是否有 \(\lfloor\dfrac{l-1}{p}\rfloor\ne\lfloor\dfrac{r}{p}\rfloor\),累加入答案即可,复杂度 \(\pi(10^7)\)。那么这个临界值是多少呢?打个表发现临界值大约在 \(2\sqrt{n}\) 左右,因此可以像根号分治一样对区间长度 \(<2\sqrt{n}\) 的询问跑线性基,对区间长度 \(\ge 2\sqrt{n}\) 的询问枚举质因子,这样复杂度就降到了 \(6000\times\dfrac{450^2}{64}+6\times 10^6\approx10^7\),即可通过此题。

总之这道题就是线性基+根号分治+猜结论的 nb tea %%%

const int MAXN=1e7;
const int MAXB=446;
const int MOD=998244353;
int pw2[MAXN+5],lim=3162;bitset<MAXN+5> vis;
int mxp[MAXN+5],pr[MAXN/10+5],prcnt=0,id[MAXN+5];
bitset<MAXB+5> hi[MAXN/10+5];bool used[MAXN/10+5];
int need[MAXN/10+5],need_n=0;
bitset<MAXB+5> b[MAXB+5];
void sieve(int n){
	for(int i=2;i<=n;i++){
		if(!vis[i]){pr[++prcnt]=i;mxp[i]=i;id[i]=prcnt;}
		for(int j=1;j<=prcnt&&pr[j]*i<=n;j++){
			vis[pr[j]*i]=1;mxp[pr[j]*i]=max(mxp[i],pr[j]);
			if(i%pr[j]==0) break;
		}
	}
}
int cnt=0;
void ins(bitset<MAXB+5> x){
	for(int i=MAXB;i;i--) if(x[i]){
		if(!b[i].any()){b[i]=x;return;}
		else x^=b[i];
	} cnt++;
}
void add(int x){
	int mx=mxp[x];if(mx>=lim) x/=mx;
	bitset<MAXB+5> bt;
	while(x!=1){
		int v=mxp[x],par=0;
		while(x%v==0) x/=v,par^=1;
		bt[id[v]]=par;
	}
	if(mx>=lim){
		if(used[id[mx]]) bt^=hi[id[mx]];
		else{
			used[id[mx]]=1;hi[id[mx]]=bt;
			need[++need_n]=id[mx];
			return;
		}
	} ins(bt);
}
void solve(){//remember to clear it at last
	int l,r;scanf("%d%d",&l,&r);
	if(r-l+1<=7000){
		for(int i=l;i<=r;i++) add(i);printf("%d\n",pw2[cnt]);
		for(int i=1;i<=need_n;i++) used[need[i]]=0,hi[need[i]].reset(),need[i]=0;
		need_n=0;for(int i=1;i<=MAXB;i++) b[i].reset();cnt=0;
	} else {
		int tot=r-l+1;
		for(int i=1;i<=prcnt;i++){
			if(pr[i]>r) break;
			if((l-1)/pr[i]!=r/pr[i]) --tot;
		} printf("%d\n",pw2[tot]);
	} 
}
int main(){
	pw2[0]=1;for(int i=1;i<=MAXN;i++) pw2[i]=2*pw2[i-1]%MOD;
	sieve(MAXN);int qu;scanf("%d",&qu);while(qu--) solve();
	return 0;
}
posted @ 2021-04-01 20:21  tzc_wk  阅读(108)  评论(0编辑  收藏  举报