CF1280H - Make Square
CF1280H - Make Square
题目大意
给定一个序列\(a_i\)
设一个数\(n\)的分解为\(n=\prod prime_i^{c_i}\)
每次查询一个区间\([l,r]\)
询问在区间内选两个数\(a_i,a_j (i\ne j)\)
使得\(a_i\cdot a_j\)的分解\(\sum (c_i\mod 2)\)最小
分析
考虑对于每个\(a_i\)求出集合\(odd(i)=\{prime_i|2\not |c_i\}\)
这个分解质因数可以在\(O(\frac{\sqrt a_i}{\ln a_i})\)的时间内完成
然后考虑选择两个数之后,实际上是求\(|odd(i)\Delta odd(j)|_{\min}\)
其中\(\Delta\)为集合对称差|异或
而容易通过按calculator发现\(|odd(i)|\leq 7\)
那么我们可以暴力枚举\(odd(i)\)与\(odd(j)\)相同的部分,剩下的部分包含的元素个数就是答案
相同的部分容易用另一个数\(x\)表示,那么实际上就是对于每个\(x\),找到删除个数最少的两个\(i,j\)
考虑依次加入每个点\(i\),单调栈维护前面\(j\)的情况
容易发现\(j\)不同的值只有\(8\)个,可以用树状数组暴力处理,复杂度为
\(O(n\cdot 2^7\cdot 7\log n+q\log n)\)
因为常数小,可以跑进1s
const int N=2e5+10,M=5.1e6+10,INF=1e9+10;
int n,m;
int stk[M][9],T[M];
int A[N];
struct BIT{
int s[N];
BIT(){ memset(s,63,sizeof s); }
void Add(int p,int x){
while(p) cmin(s[p],x),p-=p&-p;
}
int Que(int p){
int res=1e8;
while(p<=n) cmin(res,s[p]),p+=p&-p;
return res;
}
} tr;
vector <Pii> G[N];
int notpri[N],pri[N],pc;
int F[N],C,U[1<<8];
void Fac(int n){
C=0;
for(int i=1;pri[i]*pri[i]<=n;++i) if(n%pri[i]==0) {
int c=0;
while(n%pri[i]==0) c^=1,n/=pri[i];
if(c) F[C++]=pri[i];
}
if(n>1) F[C++]=n;
}
int ans[M];
int main(){
rep(i,2,N-1) if(!notpri[i]){
pri[++pc]=i;
for(int j=i;j<N;j+=i) notpri[j]=1;
}
n=rd(),m=rd();
rep(i,1,n) A[i]=rd();
rep(i,1,m){
int l=rd(),r=rd();
G[r].pb(mp(i,l));
}
rep(i,1,n) {
Fac(A[i]);
rep(S,0,(1<<C)-1){
if(S==0) U[S]=1;
else U[S]=U[S&(S-1)]*F[__lg(S&-S)];
int x=U[S],t=C-__builtin_popcount(S); // cost
rep(i,1,T[x]) {
tr.Add(stk[x][i]&((1<<20)-1),(stk[x][i]>>20)+t);
}
while(T[x] && (stk[x][T[x]]>>20)>=t) T[x]--;
stk[x][++T[x]]=t<<20|i;
}
for(auto v:G[i]) ans[v.first]=tr.Que(v.second);
}
rep(i,1,m) printf("%d\n",ans[i]);
}