题目链接:https://codeforces.com/problemset/problem/1114/F
题目大意:给你n个数,q次操作,类型1,将区间[l,r]中每个数乘以x,类型2,询问$\varphi (\prod_{i=l}^{r}a_i)$。其中初始的n个数每个小于等于300,x<=300。
Example
4 4
5 9 1 2
TOTIENT 3 3
TOTIENT 3 4
MULTIPLY 4 4 3
TOTIENT 4 4
1
1
2
emmmm,线段树肯定要用上,接下来就是对于欧拉函数的性质的运用了,对于欧拉函数$\varphi (x)$来讲,如果$x$可以表示为两个互质的数的乘积即$x=a\times b$,那么$\varphi (x)=\varphi (a)\times \varphi (b)$。如果$x$可以表示为一个质数的幂的形式即$x=p^k$,$p$为质数。那么$\varphi (p^k)=p^k-p^{k-1}=(1-p)p^{k-1}$。
那么也就是说我们可以将每个数进行质因子分解,那么则有
$sum=\varphi (p_1^{k_1}\times p_2^{k2}\times p_3^{k_3}...)=\varphi(p_1^{k_1-1})\times \varphi(p_2^{k_2-1})\times...$
$sum=(p_1-1)p_1^{k_1-1}\times (p_2-1)p_2^{k_2-1}\times (p_3-1)p_3^{k_3-1}...$
那么也就是我们只需要维护一下$p_i-1$和$p_i^{k_i-1}$就可以了,按照我自己的超级复杂的写法就是先把每个数分解了,然后在300内筛选素数,总共62个,然后用状压来表示这些数是否存在,区间融合的时候需要对左儿子和右儿子的状态进行判断,是否存在出现了相同的素数,如果出现了,我们就对维护的$p_i^{k_i-1}$里面再乘上该素数。总的来说有点复杂,时间上是$O(62qlogn)$。。。直接T13了。。。。以下是刚开始的复杂写法:
#include#include #include using namespace std; #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define lc rt<<1 #define rc rt<<1|1 typedef long long ll; const int mac=4e5+10; const ll mod=1e9+7; int a[mac],vis[350],prime[100],cnt=0; ll yes_stk=0; struct node { int l,r; ll stk,mul;//mul为剩余的p^k-1的乘积 ll fstk,fmul,orig; }tree[mac<<2]; ll qpow(ll a,ll b) { ll ans=1; a%=mod; while (b){ if (b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans%mod; } void push_up(int rt) { tree[rt].mul=tree[lc].mul*tree[rc].mul%mod; for (int i=0; i ) if ((tree[lc].stk&(1LL<i))) tree[rt].mul=tree[rt].mul*prime[i]%mod; tree[rt].stk=tree[lc].stk|tree[rc].stk; } void push_down(int rt) { for (int i=0; i ){ if ((tree[lc].fstk&(1LL<i))) tree[lc].fmul=tree[lc].fmul*prime[i]%mod; if ((tree[rc].fstk&(1LL<i))) tree[rc].fmul=tree[rc].fmul*prime[i]%mod; if ((tree[lc].stk&(1LL<i))) tree[lc].mul=tree[lc].mul*prime[i]%mod; if ((tree[rc].stk&(1LL<i))) tree[rc].mul=tree[rc].mul*prime[i]%mod; } tree[lc].fstk|=tree[rt].fstk; tree[rc].fstk|=tree[rt].fstk; tree[lc].fmul=tree[lc].fmul*tree[rt].fmul%mod; tree[rc].fmul=tree[rc].fmul*tree[rt].fmul%mod; tree[lc].orig=tree[rt].orig*tree[lc].orig%mod; tree[rc].orig=tree[rt].orig*tree[rc].orig%mod; tree[lc].stk|=tree[rt].fstk; tree[rc].stk|=tree[rt].fstk; tree[lc].mul=(tree[lc].mul*tree[rt].fmul%mod)*qpow(tree[rt].orig,tree[lc].r-tree[lc].l)%mod; tree[rc].mul=(tree[rc].mul*tree[rt].fmul%mod)*qpow(tree[rt].orig,tree[rc].r-tree[rc].l)%mod; tree[rt].fstk=0;tree[rt].fmul=1;tree[rt].orig=1; } void build(int l,int r,int rt) { tree[rt].stk=0; tree[rt].mul=1; tree[rt].fstk=0; tree[rt].fmul=1; tree[rt].orig=1; tree[rt].l=l; tree[rt].r=r; if (l==r){ int x; scanf ("%d",&x); for (int i=0; i ){ if (x==1) break; if (x%prime[i]) continue; tree[rt].stk|=1LL<<i; x/=prime[i]; while (!(x%prime[i])) tree[rt].mul=tree[rt].mul*prime[i]%mod,x/=prime[i]; } return; } int mid=(l+r)>>1; build(lson);build(rson); push_up(rt); } void update(int l,int r,int rt,int L,int R,int val) { if (l>=L && r<=R){ ll tmp=0,s=1; int x=val; tree[rt].orig=tree[rt].orig*val%mod; for (int i=0; i ){ if (x==1) break; if (x%prime[i]) continue; tmp|=1LL<<i; x/=prime[i]; while (!(x%prime[i])) s=s*prime[i]%mod,x/=prime[i]; } for (int i=0; i ){ if ((tree[rt].fstk&(1LL<i))) tree[rt].fmul=tree[rt].fmul*prime[i]%mod; if ((tree[rt].stk&(1LL<i))) tree[rt].mul=tree[rt].mul*prime[i]%mod; } tree[rt].fstk|=tmp; tree[rt].fmul=s*tree[rt].fmul%mod; tree[rt].stk|=tmp; tree[rt].mul=(tree[rt].mul*s%mod)*qpow(val,r-l)%mod; return; } int mid=(l+r)>>1; if (tree[rt].fstk) push_down(rt); if (mid>=L) update(lson,L,R,val); if (mid<R) update(rson,L,R,val); push_up(rt); } ll query(int l,int r,int rt,int L,int R) { ll ans=1; if (l>=L && r<=R){ ll tmp=1; for (int i=0; i ){ if (tree[rt].stk&(1LL<i))) { tmp=tmp*(prime[i]-1)%mod; yes_stk|=1LL<<i; } else if (tree[rt].stk&(1LL<i))) tmp=tmp*prime[i]%mod; //if (L==4 && R==5)printf("yes_stk:%d %lld %lld \n",prime[i],tmp,yes_stk ); } tmp=tmp*tree[rt].mul%mod; return tmp; } int mid=(l+r)>>1; if (tree[rt].fstk) push_down(rt); if (mid>=L) ans=ans*query(lson,L,R)%mod; if (mid mod; return ans%mod; } int main(int argc, char const *argv[]) { int m=300; int s=sqrt(m); for (int i=2; i<=s; i++) if (!vis[i]) for (int j=i*i; j<=m; j+=i) vis[j]=1; for (int i=2; i<=m; i++) if (!vis[i]) prime[cnt++]=i; int n,q; scanf ("%d%d",&n,&q); build(1,n,1); while (q--){ int l,r,x; char s[15]; scanf ("%s%d%d",s,&l,&r); yes_stk=0; if (s[0]=='M'){ scanf ("%d",&x); update(1,n,1,l,r,x); } else { printf ("%lld\n",query(1,n,1,l,r)); } } return 0; }
然后,就开始想优化了,最后能够把枚举的去掉,然后。。。。。想了想,$(p_i-1)p_i^{k_i-1}$不就是$p_i^{k_i}/p_i*(p_i-1)$吗,那么我们直接维护乘积和状态就好了,由于数据只有300,我们完全可以预处理出每个数的素因子状态,那么枚举的62次就可以直接不用了,我们再最后得出结果的时候在判断就可以了。由于中间有除法,所以还要先预处理出每个数的逆元,不过这并不费事。。。
以下是AC代码:
#include#include #include using namespace std; #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define lc rt<<1 #define rc rt<<1|1 typedef long long ll; const int mac=4e5+10; const int mod=1e9+7; int a[mac],vis[350],prime[100],cnt=0; int inv[350]; ll yes_stk=0,every_stk[350]; struct node { int l,r; ll stk,fstk; int mul,fmul; }tree[mac<<2]; int qpow(int a,int b) { int ans=1; a%=mod; while (b){ if (b&1) ans=1LL*ans*a%mod; a=1LL*a*a%mod; b>>=1; } return ans%mod; } void push_up(int rt) { tree[rt].mul=1LL*tree[lc].mul*tree[rc].mul%mod; tree[rt].stk=tree[lc].stk|tree[rc].stk; } void push_down(int rt) { tree[lc].fstk|=tree[rt].fstk; tree[rc].fstk|=tree[rt].fstk; tree[lc].fmul=1LL*tree[lc].fmul*tree[rt].fmul%mod; tree[rc].fmul=1LL*tree[rc].fmul*tree[rt].fmul%mod; tree[lc].stk|=tree[rt].fstk; tree[rc].stk|=tree[rt].fstk; tree[lc].mul=1LL*tree[lc].mul*qpow(tree[rt].fmul,tree[lc].r-tree[lc].l+1)%mod; tree[rc].mul=1LL*tree[rc].mul*qpow(tree[rt].fmul,tree[rc].r-tree[rc].l+1)%mod; tree[rt].fstk=0;tree[rt].fmul=1; } void build(int l,int r,int rt) { tree[rt].stk=0; tree[rt].mul=1; tree[rt].fstk=0; tree[rt].fmul=1; tree[rt].l=l; tree[rt].r=r; if (l==r){ int x; scanf ("%d",&x); tree[rt].stk|=every_stk[x]; tree[rt].mul=x; return; } int mid=(l+r)>>1; build(lson);build(rson); push_up(rt); } void update(int l,int r,int rt,int L,int R,int val) { if (l>=L && r<=R){ tree[rt].fstk|=every_stk[val]; tree[rt].fmul=1LL*val*tree[rt].fmul%mod; tree[rt].stk|=every_stk[val]; tree[rt].mul=1LL*tree[rt].mul*qpow(val,r-l+1)%mod; return; } int mid=(l+r)>>1; if (tree[rt].fstk) push_down(rt); if (mid>=L) update(lson,L,R,val); if (mid<R) update(rson,L,R,val); push_up(rt); } int query(int l,int r,int rt,int L,int R) { int ans=1; if (l>=L && r<=R){ yes_stk|=tree[rt].stk; return tree[rt].mul; } int mid=(l+r)>>1; if (tree[rt].fstk) push_down(rt); if (mid>=L) ans=1LL*ans*query(lson,L,R)%mod; if (mid mod; return ans%mod; } int main(int argc, char const *argv[]) { int m=300; int s=sqrt(m); for (int i=2; i<=s; i++) if (!vis[i]) for (int j=i*i; j<=m; j+=i) vis[j]=1; for (int i=2; i<=m; i++) if (!vis[i]) prime[cnt++]=i; every_stk[1]=0; for (int i=2; i<=300; i++){ ll tmp=0; int x=i; for (int j=0; j ) if (x%prime[j]) continue; else { tmp|=1LL<<j; while (!(x%prime[j])) x/=prime[j]; } every_stk[i]=tmp; } for (int i=2; i<=300; i++) inv[i]=qpow(i,mod-2); int n,q; scanf ("%d%d",&n,&q); build(1,n,1); while (q--){ int l,r,x; char s[15]; scanf ("%s%d%d",s,&l,&r); yes_stk=0; if (s[0]=='M'){ scanf ("%d",&x); update(1,n,1,l,r,x); } else { int ans=query(1,n,1,l,r); for (int i=0; i ) if (yes_stk&(1LL<<i)){ ans=(1LL*ans*inv[prime[i]]%mod)*(prime[i]-1)%mod; } printf ("%d\n",ans); } } return 0; }