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]);
}


posted @ 2021-05-30 21:03  chasedeath  阅读(104)  评论(0编辑  收藏  举报