BZOJ4026 dC Loves Number Theory
题解
什么鬼畜题目看了标题之后感觉很慌,以为是数论题,看了题目之后也没有思路,只能去求助由乃大神了。解答了一番之后感觉挺可做的。由于欧拉函数的计算公式是这样的(phi(n)=n*prod frac{pi-1}{pi}),所以我们会发现一段区间内的数乘积的欧拉函数的值只和这几个元素的质因数有关,这些区间内所有元素的质因数都会且仅会被记一次贡献(即如果有两个元素有相同的质因数,那么这个质因数做出的贡献仍然是一次)。所以我们可以先预处理出在1到(Mx)内的质数,然后剩下要做的就是统计([l,r])区间出现了那些质因数。如果没有强制在线的话倒是可以用莫队强上,但是由于题目强制在线了,我们就只能套上主席树了。主席树上每个点记录第(i)个元素每个质因数做出的贡献,但由于这样子一个区间内同一个质因数的贡献可能会被算多次,所以我们规定只把每个质因数的贡献算在最右边的一个元素上,这样就可以实现不重复贡献计算了。最后实际上就是在以(rt[r])这个线段树上查询([l,r])区间的乘积就行了。这样子预处理质数和建主席树的复杂度为(O(nsqrt{n}+nlog^2(n)))的,然后每次询问的复杂度就是(O(nlog(n)))的。不过还要注意的是,这里的主席书的空间要开(nlog^2(n))的,因为每一个点的质因数最多有(log)个,对于每个质因数修改会多出(log)个节点,所以要开(nlog^2(n))的空间。。(因为这个RE了无数遍
话说似乎还有十字链表的做法,不明觉厉。。。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('
');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
#define PAUSE printf("Press Enter key to continue..."); fgetc(stdin);
const int N=1e5+500;
const ll Md=1e6+777;
int n,q,cnt,ans;
int a[N],Mx;
int last[N],pri[Md],vis[Md],rt[N];
/*==================Define Area================*/
namespace PersistentSegmentTree {
struct node {
int ls,rs;
ll val;
}t[N*160];
int tot;
void Modify(int& newrt,int rt,int l,int r,int pos,int v) {
if(l>r) return ;
if(!newrt||newrt==rt) newrt=++tot,t[newrt].val=t[rt].val*v%Md;
else t[newrt].val=t[newrt].val*v%Md;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) {
if(!t[newrt].rs) t[newrt].rs=t[rt].rs;
Modify(t[newrt].ls,t[rt].ls,l,mid,pos,v);
}
else {
if(!t[newrt].ls) t[newrt].ls=t[rt].ls;
Modify(t[newrt].rs,t[rt].rs,mid+1,r,pos,v);
}
}
ll Query(int o,int l,int r,int L,int R) {
if(L<=l&&r<=R) return t[o].val;
int mid=(l+r)>>1;
if(R<=mid) return Query(t[o].ls,l,mid,L,R);
if(L>mid) return Query(t[o].rs,mid+1,r,L,R);
return Query(t[o].ls,l,mid,L,R)*Query(t[o].rs,mid+1,r,L,R)%Md;
}
}
using namespace PersistentSegmentTree;
ll Powe(ll x,ll y) {
ll res=1;
while(y) {
if(y&1) res*=x,res%=Md;
x*=x;x%=Md;
y>>=1;
}
return res;
}
int main() {
read(n);read(q);
for(int i=1;i<=n;i++) {
read(a[i]);
Mx=max(Mx,a[i]);
}
for(int i=2;i<=Mx;i++) {
if(!vis[i]) vis[i]=++cnt,pri[cnt]=i;
for(int j=i*2;j<=Mx;j+=i) vis[j]=-1;
}
t[0].val=1;
for(int i=1;i<=n;i++) {
if(a[i]==1) {
Modify(rt[i],rt[i-1],1,n,i,1);
continue ;
}
for(int j=1;j<=cnt&&pri[j]*pri[j]<=a[i];j++) {
if(a[i]%pri[j]==0) {
a[i]/=pri[j];Modify(rt[i],rt[i-1],1,n,i,pri[j]-1);
while(a[i]%pri[j]==0) a[i]/=pri[j],Modify(rt[i],rt[i-1],1,n,i,pri[j]);
if(last[j]) Modify(rt[i],rt[i-1],1,n,last[j],Powe(pri[j]-1,Md-2)*pri[j]%Md);
last[j]=i;
}
}
if(a[i]>1) {
int j=vis[a[i]];
if(last[j]) Modify(rt[i],rt[i-1],1,n,last[j],Powe(pri[j]-1,Md-2)*pri[j]%Md);
last[j]=i;Modify(rt[i],rt[i-1],1,n,i,pri[j]-1);
}
}
for(int i=1,l,r;i<=q;i++) {
read(l);read(r);
l^=ans,r^=ans;
ans=Query(rt[r],1,n,l,r);
printf("%d
",ans);
}
return 0;
}