20200502省选模拟赛 C (莫队+数值分治)

20200502省选模拟赛 C (莫队+数值分治)_第1张图片

 

 

题解

或许这就是人生吧

这题似乎比[Ynoi2015]此时此刻的光辉更毒瘤

一些数的子集的gcd很难直接计算

我们就来考虑每种质因子的贡献

则答案就是

\prod_{p\in prime}\prod_{k=1}(p^k)^{f[p^k]-f[p^{k+1}]}

f[p^k]表示在这段数中有多少个子集的gcd被p^k整除

显然f[p^k]=2^g[p^k]-1,g[p^k]表示这段区间中有多少个数被p^k整除

稍微变换一下形式

\prod_{p\in prime}\prod_{k=1}p^{k*(f[p^k]-f[p^{k+1}])}

\prod_{p\in prime}p^{\sum_{k=1}k*(f[p^k]-f[p^{k+1}])}

\prod_{p\in prime}p^{\sum_{k=1}{f[p^k]}

最后的这个形式就比较简单

我们如果直接用莫队,时间复杂度将会是O(n*sqrt(m)*(sqrt(w)/ln(w))*logw+m)(有一个枚举质因子以及其幂次k复杂度和一个快速幂复杂度)

最后的那个logw可以通过预处理来消掉

至于sqrt(w)/ln(w)

我们可以对数值进行分治

把<=sqrt(w)的质数单独拿出来,把它们的可能的所有幂次求出来,做一个前缀和

就可以把这一部分的复杂度分一点到后面的O(m)

>sqrt(w)的质数由于每个数只有一个,所以可以O(1)加入

所以复杂度就平衡为O(n*sqrt(m)+m*2sqrt(sqrt(w))/ln(w))

代码:(细节超多)(vector要reverse否则会爆内存)

#include
#include
#include
#include
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define D 331
const int mod=998244353;
int n,m,a[N];
int prime[N],tot;
bool vis[N];int num[N];
int sum[180][N],pr[180],scnt;
//prework: calculate the pw and inv of prime>=sqrt(n)
int tong[N];vector pw[N],inv[N];
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
void shai()
{
	int i,j,k;
	vis[1]=1;
	for(i=2;i<=100000;i++){
		if(!vis[i])prime[++tot]=i;
		for(j=1;j<=tot;j++){
			int tmp=i*prime[j];
			if(tmp>100000)break;
			vis[tmp]=1;
			if(i%prime[j]==0)break;
		}
	}
	for(i=1;i<=tot;i++){
		int x=prime[i];
		if(x<=D){
			for(j=x;j<=100000;j*=x){
				pr[++scnt]=x;
				for(k=1;k<=n;k++){
					sum[scnt][k]=sum[scnt][k-1];
					if(a[k]%j==0)sum[scnt][k]++;
				}
				if(j==x){
					pw[x].reserve(sum[scnt][n]+1);
					pw[x].push_back(x);
					for(k=1;k<=sum[scnt][n];k++)
						pw[x].push_back(1ll*pw[x][k-1]*pw[x][k-1]%mod);
					for(k=1;k<=sum[scnt][n];k++)
						pw[x][k]=1ll*pw[x][k]*pw[x][k-1]%mod;
				}
			}
		}
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=tot;j++){
			if(prime[j]>D)break;
			while(a[i]%prime[j]==0)
				a[i]/=prime[j];
		}
		tong[a[i]]++;
	}
	for(i=1;i<=100000;i++){
		int x=i;
		if(tong[x]){
			pw[x].reserve(tong[x]+1);inv[x].reserve(tong[x]+1);
			pw[x].push_back(x);inv[x].push_back(ksm(x,mod-2));
			for(j=1;j<=tong[x];j++){
				pw[x].push_back(1ll*pw[x][j-1]*pw[x][j-1]%mod);
				inv[x].push_back(1ll*inv[x][j-1]*inv[x][j-1]%mod);
			}
		}
	}
}
int bel[N];
struct node{
	int l,r,id;
	bool operator < (const node &t)const{
		int f=bel[l];
		return ft.r)));
	}
}q[N];
int cnt[N],ans,mul;
void add(int x)
{
	if(x==1)return;
	ans=1ll*ans*pw[x][cnt[x]]%mod;
	cnt[x]++;
}
void del(int x)
{
	if(x==1)return;
	cnt[x]--;
	ans=1ll*ans*inv[x][cnt[x]]%mod;
}
int lan[N];
int main()
{
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
	int i,j,l,r;
	n=gi();m=gi();
	for(i=1;i<=n;i++){a[i]=gi();bel[i]=(i-1)/D+1;}
	for(i=1;i<=m;i++){q[i].l=gi();q[i].r=gi();q[i].id=i;}
	sort(q+1,q+m+1);
	shai();
	ans=mul=1;l=q[1].l;r=q[1].r;
	for(i=q[1].l;i<=q[1].r;i++)add(a[i]);
	for(i=1;i<=scnt;i++)
		if(sum[i][r]-sum[i][l-1]>0)
			mul=1ll*mul*pw[pr[i]][sum[i][r]-sum[i][l-1]-1]%mod;
	lan[q[1].id]=1ll*ans*mul%mod;
	for(i=2;i<=m;i++){
		while(rq[i].l)l--,add(a[l]);
		while(r>q[i].r)del(a[r]),r--;
		while(l0)
				mul=1ll*mul*pw[pr[j]][sum[j][r]-sum[j][l-1]-1]%mod;
		lan[q[i].id]=1ll*ans*mul%mod;
	}
	for(i=1;i<=m;i++)
		printf("%d\n",lan[i]);
}

 

 

 

 

 

 

 

 

你可能感兴趣的:(数据结构,数学,分治)