[自测]LCM

题目

多次询问区间\([l,r]\),求区间所有数的\(lcm\),答案对\(10^9+7\)取模,强制在线,\(l,r\leq 10^5\)

解法1

构造一个数组\(d_i\),对每个质数的开一个栈,记录它出现的位置

对于位置\(i\)构造一个\(d_i\),如果\(i\)有一个质因子\(p^k\),将栈中的前\(k\)个元素弹出,加入这个质因子,并令\(d_i=i\)
对于被弹出的元素,假设它的位置是\(l\),那么\(d_l/=p\)

对于询问\([l,r]\),只构造到\(d_r\),那么\(\prod_{i=l}^r d_i\)即为答案,用线段树维护区间乘法

可以发现\([1,r]\)状态由\([1,r-1]\)继承,所以用可持久化线段树即可

时间复杂度\(O(qlog^2n)\),空间复杂度\(O(nlogn)\)

解法2

定义数组\(w_i\),如果\(i=p^k\),那么\(w_i=p\),否则\(w_i=1\)

对于询问\([l,r]\)\(w_{p^k}\)有贡献当且仅当\(p^k\)的倍数落在\([l,r]\)里面,即\(\lfloor \frac{l-1}{p^k} \rfloor \neq \lfloor \frac{r}{p^K} \rfloor\)

那么\(ans_{[l,r]}=\prod_{i=1}^r\times [\lfloor \frac{l-1}{p^k} \rfloor \neq \lfloor \frac{r}{p^K} \rfloor]\)

时间复杂度\(O(q\sqrt{n})\),空间复杂度\(O(n)\)

#include<bits/stdc++.h>
#define N 100005
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int T,n,k,A,B,mo,c[200005],d[200005];
int w[N],p[N],cnt;
ll pre[N],inv[N],las;
bool isnotp[N];

template <class T> inline T Max(T a,T b) { return a > b ? a : b; }
template <class T> inline T Min(T a,T b) { return a < b ? a : b; }
template <class T> void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
ll qp(ll a,ll b) { ll ret=1; for(;b;b>>=1,a=a*a%mod) if(b&1) ret=ret*a%mod; return ret; }
void init(int maxn)
{
	w[1]=1;
	for(int i=2;i<=maxn;++i)
	{
		if(!isnotp[i]) p[++cnt]=i;
		for(int j=1;j<=cnt&&(ll)p[j]*i<=maxn;++j)
		{
			w[p[j]*i]=1;
			isnotp[p[j]*i]=1;
			if(i%p[j]==0) break;
		}
	}
	for(int i=1;i<=cnt;++i)
	  for(ll x=p[i];x<=maxn;x*=p[i]) w[x]=p[i];
	pre[0]=1;inv[0]=1;
	for(int i=1;i<=maxn;++i) pre[i]=pre[i-1]*w[i]%mod;
	for(int i=1;i<=maxn;++i) inv[i]=qp(pre[i],mod-2);
	
//	for(int i=1;i<=100;++i) cout<<i<<' '<<w[i]<<endl;
}
void solve(int n,int k)
{
	int L=n-k,R=n;
	ll ans=1;
	for(int l=1,r;l<=R;l=r+1)
	{
		if(L/l) r=Min(R/(R/l) , L/(L/l));
		else r=R/(R/l);
		if(L/l != R/l) ans=ans * pre[r]%mod * inv[l-1]%mod;
	}
	las=ans;
	printf("%lld\n",ans);
}
int main()
{
	freopen("num.in","r",stdin);
	freopen("num.out","w",stdout);
	init(N-5);
	read(T); --T;
	read(n);read(k);
	solve(n,k);
	read(A);read(B);read(mo);
	for(int i=1;i<=T;++i) read(c[i]);
	for(int i=1;i<=T;++i) read(d[i]);
	for(int i=1;i<=T;++i)
	{
		n=(A*las+c[i])%mo+1;
		k=(B*las+d[i])%n+1;
		solve(n,k);
	}
	return 0;
}
posted @ 2019-12-05 12:12  擅长平地摔的艾拉酱  阅读(240)  评论(0编辑  收藏  举报
/*取消选中*/