洛谷P5071 [YNOI2015]此时此刻的光辉 莫队+玄学优化+卡常QWQ

题目链接:传送门

在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。

珂朵莉最可爱了,珂朵莉的题最毒瘤了qwq

在这里插入图片描述

然而这道题……
在这里插入图片描述
首先这道题显然是莫队qwq。
把质数筛出来,把每个数大莉分解质因数。
n n n分解质因数为 p 1 a 1 ∗ p 2 a 2 ∗ . . . p k a k p_1^{a_1}*p_2^{a_2}*...p_k^{a_k} p1a1p2a2...pkak,则 n n n的因数个数为 Π i = 1 k ( a i + 1 ) \Large\Pi\large_{i=1}^{k}(a_i+1) Πi=1k(ai+1)(简单,不解释)。
把每个数的质因数和它的指数存在一个 v e c t o r vector vector里面。
每次统计的时候珂以大莉遍历每一个质因数,然后暴莉统计答案。
然后……稳稳的TLE……QAQ
发现珂以把逆元线性筛出来,于是复杂度降一个 O ( l o g n ) O(logn) O(logn)……
然后……还是稳稳的TLE……QAQ
用上所有优化常数的办法,发现一直卡在82分qwq
于是我们可以考虑上网搜题解。

搜出一个玄学优化:
之前的暴莉莫队是把所有指数的出现次数大莉统计出来。
发现比较小的质数的指数较大,所以能不能预处理出这一部分质数的指数呢?
发现是珂以的qwq
s u m [ i ] [ j ] sum[i][j] sum[i][j]表示前 i i i个数的乘积,第 j j j个质数的指数是多少。
发现 s u m [ i ] [ j ] sum[i][j] sum[i][j]就是一个指数的前缀和qwq。
所以每次用前缀和算出 &lt; = 160 &lt;=160 <=160的质数的指数之积,然后其他指数用莫队来求即可。
(别问我为什么是 160 160 160,题解大法好qwq)
时间复杂度……大概 O ( n n ) O(n\sqrt n) O(nn )

代码

//LXL毒瘤~
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<vector>
#include<map>
#define re register int
#define rl register ll
#define mod 19260817
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=10*x+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline void write(int x) {
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
namespace I_Love {

const int Size=100005;
const int L=160;
int n,m,siz,a[Size],belong[Size],inv[2000005];
struct Query {
	int l,r,id;
} Q[Size];
inline bool comp(Query x,Query y) {
	if(belong[x.l]!=belong[y.l])	return x.l<y.l;
	return x.r<y.r;
}
int tot,prime[1000005];
int sum[Size][161];
bool vis[Size*10];
map<int,int> pos;
void GetPrime(int maxn) {
	//筛质数 
	vis[1]=true;
	//卡常,除2以外的偶数不是质数,先处理出来 
	for(re i=4; i<=maxn; i+=2)	vis[i]=true;
	prime[++tot]=2; pos[2]=1;
	for(re i=3; i<=maxn; i+=2) {
		if(!vis[i]) {
			prime[++tot]=i;
			pos[i]=tot;
			for(re j=i*i; j<=maxn; j+=i) {
				vis[j]=true;
			}
		}
	}
}
struct Prime {
	int id,t;
};
vector<Prime> div[Size];
void Divide(int x) {
	//对a[x]分解质因数 
	int num=a[x];
	//注意这里要写成prime[i]*prime[i]<=num 
	for(re i=1; i<=tot && prime[i]*prime[i]<=num; i++) {
		int now=0;
		while(!(num%prime[i])) {
			num/=prime[i];
			now++;
		}
		if(now) {
//			div[x].push_back(tmp);
			if(i<=L) {
				sum[x][i]+=now;
			} else {
				Prime tmp; tmp.id=i; tmp.t=now;
				div[x].push_back(tmp);
			}
		}
	}
	if(num!=1) {
		int id=pos[num];
		if(!id) {
			id=pos[num]=++tot;
			prime[tot]=num;
		}
		if(id<=L) {
			sum[x][id]++;
		} else {
			Prime tmp; tmp.id=id; tmp.t=1;
			div[x].push_back(tmp);
		}
	}
}
ll ans,c[Size<<1];
int out[Size<<1];
inline void add(int x) {
	int len=div[x].size();
	for(re i=0; i<len; i++) {
		Prime now=div[x][i];
		ans=ans*(ll)inv[c[now.id]]%mod;
		c[now.id]+=now.t;
		ans=ans*c[now.id]%mod;
	}
}
inline void del(int x) {
	int len=div[x].size();
	for(re i=0; i<len; i++) {
		Prime now=div[x][i];
		ans=ans*(ll)inv[c[now.id]]%mod;
		c[now.id]-=now.t;
		ans=ans*c[now.id]%mod;
	}
}
void Fujibayashi_Ryou() {
	n=read();
	m=read();
	siz=n/sqrt(m*2.0/3.0);
	int maxa=0;
	for(re i=1; i<=n; i++) {
		a[i]=read();
		belong[i]=i/siz+1;
		if(a[i]>maxa) {
			maxa=a[i];
		}
	}
	GetPrime(sqrt(maxa));
	for(re i=1; i<=n; i++) {
		//预处理前缀和 
		for(re j=0; j<=L; j++)	sum[i][j]=sum[i-1][j];
		Divide(i);
		int len=div[i].size();
		for(re j=0; j<len; j++) {
			Prime now=div[i][j];
			c[now.id]+=now.t;
		}
	}
	int maxn=0;
	for(re i=1; i<=tot; i++) {
		if(c[i]>maxn) {
			maxn=c[i];
		}
	}
	maxn++;
	inv[0]=inv[1]=1;
	//线性求逆元 
	for(re i=2; i<=maxn; i++)
		inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
	for(re i=1; i<=tot; i++)	c[i]=1;
	for(re i=1; i<=m; i++) {
		Q[i].l=read();
		Q[i].r=read();
		Q[i].id=i;
	}
	sort(Q+1,Q+1+m,comp);
	int l=1,r=0;
	ans=1;
	for(re i=1; i<=m; i++) {
		while(r<Q[i].r)	add(++r);
		while(r>Q[i].r)	del(r--);
		while(l<Q[i].l)	del(l++);
		while(l>Q[i].l)	add(--l);
		out[Q[i].id]=ans;
		for(re j=1; j<=L; j++) {
			out[Q[i].id]=(ll)out[Q[i].id]*(sum[Q[i].r][j]-sum[Q[i].l-1][j]+1)%mod;
		}
	}
	for(re i=1; i<=m; i++) {
		printf("%d\n",out[i]);
	}
}

}
int main() {
	I_Love::Fujibayashi_Ryou();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值