20200502省選模擬賽 C (莫隊+數值分治)

 

 

題解

或許這就是人生吧

這題似乎比[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<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
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<int> 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 f<bel[t.l]||(f==bel[t.l]&&(((f&1)&&r<t.r)||(!(f&1)&&r>t.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(r<q[i].r)r++,add(a[r]);
		while(l>q[i].l)l--,add(a[l]);
		while(r>q[i].r)del(a[r]),r--;
		while(l<q[i].l)del(a[l]),l++;
		mul=1;
		for(j=1;j<=scnt;j++)
			if(sum[j][r]-sum[j][l-1]>0)
				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]);
}

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章