LOJ 2978 「THUSCH 2017」杜老师——bitset+线性基+结论
题目:https://loj.ac/problem/2978
题解:https://www.cnblogs.com/Paul-Guderian/p/10248782.html
第 i 个数的 bitset 的第 j 位表示 i 是否含有奇数个 “第 j 个质数” 。
想到用 bitset ,就开始考虑怎样 DP ……
其实是求选一些数,使得它们的 bitset 异或和为 0 。所以求线性基,答案就是 2R-L+1-线性基大小 。
然后考虑根号分治。
大于 \( \sqrt{n} \) 的质数,每个数最多含有其一次方。所以一个大于 \( \sqrt{n} \) 的质数一旦出现,线性基里就一定有它。
所以对小于 \( \sqrt{n} \) 的约 500 个质数做线性基。
找出每个数的最大质因子,如果是大于 \( \sqrt{n} \) 的,直接 dec++(dec 是记录的线性基大小);如果该质因子已经出现过,就把该数的剩余部分异或掉该质因子第一次出现的那个数的剩余部分,然后尝试加入线性基。
似乎区间长度 >= 6000 的话,一个质因子一旦出现就一定会在线性基里。判断小于 \( \sqrt{n} \) 的质因子是否出现,只需看 (L-1)/pri < R/pri 。
#include<cstdio> #include<cstring> #include<algorithm> #include<bitset> #include<cmath> #define ll long long using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } const int N=505,M=1e7+5,mod=998244353; int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;} int L,R,lm,pri[M],cnt,mxd[M],mnd[M],dy[M]; int q[6005],dfn[M],bin[M];bool vis[M]; bitset<N> bs[N],nw,pr; bool cmp(int u,int v){return mxd[u]<mxd[v];} void init() { int n=1e7;lm=sqrt(n); ll d; for(int i=2;i<=n;i++) { if(!vis[i]){ pri[++cnt]=i;dy[i]=cnt;mxd[i]=mnd[i]=i;} for(int j=1;j<=cnt&&(d=(ll)i*pri[j])<=n;j++) { vis[d]=1; mnd[d]=pri[j]; mxd[d]=mxd[i]; if(i%pri[j]==0)break; } } for(int i=1;i<=cnt;i++) if(pri[i]>lm){cnt=i-1;break;} bin[0]=1;for(int i=1;i<=n;i++)bin[i]=upt(bin[i-1]<<1); } void get(int x) { if(mxd[x]>lm)x/=mxd[x]; nw.reset(); while(x>1) { int t=0,d=mnd[x]; while(mnd[x]==d)x/=d,t++; if(t&1)nw.set(dy[d]-1); } } int Ins() { for(int i=0;i<cnt;i++) if(nw[i]) { if(bs[i][i])nw^=bs[i]; else {bs[i]=nw;return 1;} } return 0; } void solve1() { int tot=0; for(int i=L;i<=R;i++)q[++tot]=i; sort(q+1,q+tot+1,cmp); for(int i=0;i<cnt;i++)bs[i].reset(); int dec=0,d2=0; for(int i=1,cr;i<=tot;i++) { cr=q[i]; get(cr); if(mxd[cr]<=lm)dec+=Ins(); else if(mxd[cr]!=mxd[q[i-1]]) { d2++; pr=nw;} else { nw^=pr; dec+=Ins();} if(dec==cnt+1)break;//can't cal d2!!! } printf("%d\n",bin[tot-dec-d2]); } void solve2(int tim) { int dec=0; for(int i=L;i<=R;i++) { int cr=mxd[i]; if(cr>lm&&dfn[cr]!=tim)dfn[cr]=tim,dec++; } for(int i=1;i<=cnt;i++) if((L-1)/pri[i]<R/pri[i])dec++; printf("%d\n",bin[R-L+1-dec]); } int main() { init(); int T=rdn(); while(T--) { L=rdn(),R=rdn(); if(R-L+1>=6000)solve2(T+1); else solve1(); } return 0; }