[FJWC2018]欧拉函数
歐拉函數
題目描述
題解
首先,我們關注到這一題最重要的一點是保證所有的a,i,x,l,ra,i,x,l,ra,i,x,l,r均隨機生成。
對于操作111,每次詢問的區間和大小期望大概是na4≈5×108\frac{na}{4}\approx 5\times 10^84na?≈5×108,區間的和。
如果我們暴力算歐拉函數大概是O(5×108log?5×108≈4152.3)O(\sqrt{\frac{5\times 10^8}{\log 5\times 10^8}}\approx 4152.3)O(log5×1085×108??≈4152.3)的,對于q=105q=10^5q=105,應該也勉強能過。
由于總和是隨機的,所以應該也不需要所有質數都跑完,會更快一點。
關鍵是操作222,對于乘法我們又該怎樣統計。
由于隨機生成,所以我們可以猜測每個數所擁有的大于303030的質因子個數應該比較少,每個數大概期望有1.051.051.05個。
故我們很快可以想到將小于303030的質數貢獻與大于303030的質數貢獻分別進行處理。
對于小于303030的質數,由于個數比較小,我們可以考慮用線段樹單獨維護每個質因子的出現次數。
由于歐拉函數是積性函數,所以我們顯然可以對于單個質因子分別統計歐拉函數值,最后乘起來。
詢問222查詢時就直接查詢區間內這部分質數出現了多少個,計算該質數貢獻的歐拉函數就行了,大概是O(10log?n)O\left(10\log\,n\right)O(10logn)的。
對于大于303030的質數,由于本身就比較多,相同質因子的應該也比較分散。
對于一個數的質因子ppp,如果它前面有數也存在ppp的質因子,那么它的貢獻時ppp,否則是p?1p-1p?1。
對于前面是否存在數有質因子ppp,顯然我們只需要看離它最近的一個ppp是不是在我們查詢的區間范圍內。
我們在查詢到它的時候可以在前面的ppp的位置放上一個pp?1\frac{p}{p-1}p?1p?,在它原來的位置就放p?1p-1p?1,查詢左邊界的后綴積,顯然只會在包含前面的哪個位置時它的貢獻才會是ppp,否則其貢獻為p?1p-1p?1。
這種技巧我們應該是在前面的題中見到過得,但我懶得找了。
但如果我們真的一個一個去查詢的話顯然太慢了,每一個都得拿一個數據結構維護,我們不妨考慮分塊。
對于塊內的點,如果它們不存在大于303030的公共質因子,顯然它們之間是獨立的,我們完全可以將它們維護的這個貢獻給放在一起。
如果同一個塊內兩個數有著相同的質因子p?1p-1p?1,顯然后面那一個是不可能產生p?1p-1p?1的貢獻的,我們就將這個整塊的貢獻乘上p(p?1)p(p-1)p(p?1),再在前一個數的前一個相同質因子位置上放上pp?1\frac{p}{p-1}p?1p?就行了。
這樣的話,我們對于一個整塊就只需要維護一個數據結構了,這個數據結構采用樹狀數組就行了。
對于散塊的點,我們需要知道的是它們前面是否有與它相同的質因子的數,這個數既可能來自整塊的數,也有了能來自散塊的數。
散塊的數就直接將它們大于303030的質因子記錄下來就行了,對于整塊,我們可以去記錄一下整塊的數關于大于303030的每個質數的前綴和,通過差分找到我們現在的這個質數是否出現。
由于我們的修改次數是比較少的,我們可以在每次修改后暴力修正修改的這個質數的前綴和。
同時修改時對于整塊我們可能會影響它自身與它相同質因子的后面最近的點關于整塊數據結構內的貢獻。
可以用setsetset維護它后面的點是哪一個,然后暴力在數據結構上更改。
這樣就很容易計算出整塊與散塊的貢獻了,與小于303030質數的貢獻乘在一起就行了。
記我們進行了mmm次000類操作,有PPP個質數,SSS為塊長,時間復雜度O(nSP+m(nS+10log?n)+q(10log?n+S+nSlog?n)?O(10(m+q)log?n+qnlog?n))O\left(\frac{n}{S}P+m(\frac{n}{S}+10\log\,n)+q(10\log\,n+S+\frac{n}{S}\log\,n)\geqslant O\left(10(m+q)\log\,n+q\sqrt{n\log\,n}\right)\right)O(Sn?P+m(Sn?+10logn)+q(10logn+S+Sn?logn)?O(10(m+q)logn+qnlogn?)),顯然可以過。
源碼
有點卡常。
#pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #include<bits/stdc++.h> using namespace std; #define MAXN 50005 #define MAXM 500005 #define lowbit(x) (x&-x) #define reg register #define pb push_back #define mkpr make_pair #define fir first #define sec second #define lson (rt<<1) #define rson (rt<<1|1) typedef long long LL; typedef unsigned long long uLL; typedef long double ld; typedef pair<int,int> pii; const int INF=0x3f3f3f3f; const int mo=1e9+7; const int mod=1e5+3; const int inv2=5e8+4; const int jzm=2333; const int zero=20000; const int n1=400; const int orG=3,ivG=332748118; const long double Pi=acos(-1.0); const double eps=1e-12; template<typename _T> _T Fabs(_T x){return x<0?-x:x;} char gc(){static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;} #define getchar gc char obuf[1<<22],*opt=obuf+(1<<22); void pc(const int&ch){*--opt=ch;} #define putchar pc template<typename _T> void read(_T &x){_T f=1;x=0;char s=getchar();while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}x*=f; } template<typename _T> void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');} int gcd(int a,int b){return !b?a:gcd(b,a%b);} int add(int x,int y,int p){return x+y<p?x+y:x+y-p;} void Add(int &x,int y,int p){x=add(x,y,p);} int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;} const int M=255; int n,q,a[MAXN],prime[MAXN],cntp,ff[MAXN],sta[MAXN],stak,tur[MAXN]; int block[MAXN],L[MAXN],R[MAXN],summ[MAXN],cnt[M][5005],ans[MAXN*2],tott; int head[MAXN],nxt[MAXN*10],val[MAXN*10],tot; set<int>s[MAXN]; set<int>::iterator it; bool oula[MAXN],vis[MAXN]; void init(){for(int i=2;i<=4e4;i++){if(!oula[i]){prime[++cntp]=i,tur[i]=cntp;}for(int j=1;j<=cntp&&1ll*prime[j]*i<=4e4;j++){oula[i*prime[j]]=1;if(i%prime[j]==0)break;}}ff[1]=1;for(int i=2;i<=4e4;i++)ff[i]=1ll*(mo-mo/i)*ff[mo%i]%mo; } void work(int id){head[id]=0;int now=a[id];for(int i=1;i<=cntp&&1ll*prime[i]*prime[i]<=now;i++){if(now%prime[i])continue;if(i>10){val[++tot]=prime[i];nxt[tot]=head[id];head[id]=tot;}while(now%prime[i]==0)now/=prime[i];}if(now>prime[10]){val[++tot]=now;nxt[tot]=head[id];head[id]=tot;} } class BitTree{private:int tr[MAXN];public:void insert(int pos,int aw){while(pos<=n)tr[pos]+=aw,pos+=lowbit(pos);}int query(int pos){int res=0;while(pos)res+=tr[pos],pos-=lowbit(pos);return res;} }pT[26],T; class MulBitTree{private:int tr[MAXN],ip;public:void build(int id){ip=id;for(int i=1;i<=n;i++)tr[i]=1;}void insert(int pos,int aw){while(pos)tr[pos]=1ll*aw*tr[pos]%mo,pos-=lowbit(pos);}int query(int pos){int res=1;while(pos<=n)res=1ll*res*tr[pos]%mo,pos+=lowbit(pos);return res;} }Bp[M]; void misakaAdd(int x,const int y,const int z){if(y==block[x])summ[y]=1ll*z*summ[y]%mo;else summ[y]=1ll*(z-1)*summ[y]%mo,Bp[y].insert(x,1ll*ff[z-1]*z%mo); } void misakaDel(int x,const int y,const int z){if(y==block[x])summ[y]=1ll*ff[z]*summ[y]%mo;else summ[y]=1ll*ff[z-1]*summ[y]%mo,Bp[y].insert(x,1ll*ff[z]*(z-1)%mo); } int sakura(int l,int r){int res=1;for(int j=l;j<=r;j++){int now=a[j];for(int k=head[j];k;k=nxt[k]){const int z=val[k],ip=tur[z];int num=0;while(now%z==0){now/=z;num++;if(vis[ip])res=1ll*z*res%mo;else res=1ll*(z-1)*res%mo,vis[ip]=1,sta[++stak]=ip;}}}return res; } int sakura2(int l,int r,const int al,const int ar){int res=1;for(int j=l;j<=r;j++){int now=a[j];for(int k=head[j];k;k=nxt[k]){const int z=val[k],ip=tur[z];int num=0;while(now%z==0){now/=z;num++;if(vis[ip]||cnt[ar][ip]>cnt[al][ip])res=1ll*z*res%mo;else res=1ll*(z-1)*res%mo,vis[ip]=1,sta[++stak]=ip;}}}return res; } signed main(){freopen("phi.in","r",stdin);freopen("phi.out","w",stdout);read(n);read(q);init();for(int i=1;i<=n;i++)read(a[i]);for(int i=1;i<=n;i++)block[i]=(i+n1-1)/n1;for(int i=1;i<=n;i++){if(!L[block[i]])L[block[i]]=i;R[block[i]]=i;}for(int i=1;i<=block[n];i++)Bp[i].build(i),summ[i]=1;for(int i=1;i<=n;i++){T.insert(i,a[i]);int now=a[i];work(i);for(int j=1;j<=10;j++){int tmp=0;while(now%prime[j]==0)now/=prime[j],tmp++;if(tmp)pT[j].insert(i,tmp);}const int ip=block[i];for(int j=head[i];j;j=nxt[j]){int num=0;const int x=val[j];while(now%x==0){s[x].insert(3*i+num);now/=x;it=s[x].lower_bound(3*i+num);num++;if(it!=s[x].begin()){it--;int vp=(*it)/3;misakaAdd(vp,ip,x);}else summ[ip]=1ll*(x-1)*summ[ip]%mo;}cnt[ip][tur[x]]+=num;}}for(int i=2;i<=block[n];i++)for(int j=11;j<=cntp;j++)cnt[i][j]+=cnt[i-1][j];for(int i=1;i<=q;i++){int opt,x,y;read(opt);read(x);read(y);if(opt==0){for(int j=1;j<=10;j++){int nowx=a[x],nowy=y,numx=0,numy=0; while(nowx%prime[j]==0)nowx/=prime[j],numx++;while(nowy%prime[j]==0)nowy/=prime[j],numy++;pT[j].insert(x,numy-numx);}int now=a[x];const int ip=block[x];for(int j=head[x];j;j=nxt[j]){int num=0;const int z=val[j];while(now%z==0){it=s[z].lower_bound(3*x+num);now/=z;int pp=0;if(it!=s[z].begin()){it--;int vp=(*it)/3;pp=vp;misakaDel(vp,ip,z);it++;}else summ[ip]=1ll*ff[z-1]*summ[ip]%mo;it++;if(it!=s[z].end()){int vp=(*it)/3;misakaDel(x,block[vp],z);if(pp)misakaAdd(pp,block[vp],z);else summ[block[vp]]=1ll*(z-1)*summ[block[vp]]%mo;}s[z].erase(3*x+num);num++;}for(int k=ip;k<=block[n];k++)cnt[k][tur[z]]-=num;}T.insert(x,y-a[x]);a[x]=y;work(x);now=y;for(int j=head[x];j;j=nxt[j]){int num=0;const int z=val[j];while(now%z==0){s[z].insert(3*x+num);it=s[z].lower_bound(3*x+num);num++;now/=z;int pp=0;if(it!=s[z].begin()){it--;int vp=(*it)/3;pp=vp;misakaAdd(vp,ip,z);it++;}else summ[ip]=1ll*(z-1)*summ[ip]%mo;it++;if(it!=s[z].end()){int vp=(*it)/3;if(pp)misakaDel(pp,block[vp],z);else summ[block[vp]]=1ll*ff[z-1]*summ[block[vp]]%mo;misakaAdd(x,block[vp],z);}}for(int k=ip;k<=block[n];k++)cnt[k][tur[z]]+=num;}}if(opt==1){int tmp=T.query(y)-T.query(x-1),res=1;for(int j=1;j<=cntp&&1ll*prime[j]*prime[j]<=tmp;j++){if(tmp%prime[j])continue;int num=0;while(tmp%prime[j]==0)tmp/=prime[j],num++;res=1ll*(prime[j]-1)*qkpow(prime[j],num-1,mo)%mo*res%mo;}if(tmp>1)res=1ll*(tmp-1)*res%mo;ans[++tott]=res;}if(opt==2){int res=1;for(int j=1;j<=10;j++){int tmp=pT[j].query(y)-pT[j].query(x-1);if(!tmp)continue;res=1ll*(prime[j]-1)*res%mo;res=1ll*res*qkpow(prime[j],tmp-1,mo)%mo;}if(block[x]==block[y]){res=1ll*res*sakura(x,y)%mo;ans[++tott]=res;while(stak)vis[sta[stak--]]=0;continue;}res=1ll*res*sakura(x,R[block[x]])%mo;for(int j=block[x]+1;j<block[y];j++)res=1ll*res*summ[j]%mo,res=1ll*res*Bp[j].query(x)%mo;res=1ll*res*sakura2(L[block[y]],y,block[x],block[y]-1)%mo;ans[++tott]=res;while(stak)vis[sta[stak--]]=0;}}while(tott)print(ans[tott--]);fwrite(opt,1,obuf+(1<<22)-opt,stdout);return 0; }謝謝!!!
總結
以上是生活随笔為你收集整理的[FJWC2018]欧拉函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 觅知网ppt模板_有哪些相见恨晚的PPT
- 下一篇: LaTex常用语法