1. 程式人生 > >hdu 4407 Sum(容斥原理+數論)

hdu 4407 Sum(容斥原理+數論)

首先求出1~N之內與P互質的數的和,然後儲存每次改變的操作。如遇到詢問,dfs搜尋一下更新ans值即可。

在求1~N之內與P互質的數的和的時候,反著先求與P不互質的數的和,再用總和減去要方便很多。而求與P不互質的數可以用容斥原理來實現,再用公式求和來進行更新即可。

處理起來頗為複雜,不失為一道好題。

#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
#define maxn 400005
typedef long long LL;
int prime[maxn][15]={0
},vis[maxn]={0}; LL ans; map<int,int>s; map<int,int>::iterator it; int gcd(int a,int b) //求最大公約數 { return a%b?gcd(b,a%b):b; } void EulerPrime() //求尤拉函式值和每個數的素因子 { for(int i=2;i<maxn;i++) if(!vis[i]) { prime[i][++prime[i][0]]=i; for
(int j=2;j*i<maxn;j++) { vis[i*j]=1; prime[i*j][++prime[i*j][0]]=i; } } } void dfs(int id,int num,int cnt,int m,int n,int k) //dfs的容斥原理 { if(cnt==m) { int k=n/num; LL res=(LL)num*k*(k+1)/2; ans+=m%2?-res:res; } else
if(id<=prime[k][0]&&num<=n) { dfs(id+1,num,cnt,m,n,k); dfs(id+1,num*prime[k][id],cnt+1,m,n,k); } } LL get(int n,int k) //求和 { if(n<=0) return 0; ans=(LL)n*(n+1)/2; for(int i=1;i<=prime[k][0];i++) dfs(1,1,0,i,n,k); return ans; } int main() { int t,n,m,k,x,y,c; scanf("%d",&t); EulerPrime(); while(t--) { scanf("%d%d",&n,&m); s.clear(); while(m--) { scanf("%d",&k); if(k==1) { scanf("%d%d%d",&x,&y,&c); LL res=get(y,c)-get(x-1,c); for(it=s.begin();it!=s.end();it++) { if((*it).first>=x&&(*it).first<=y) { if(gcd(c,(*it).first)==1) res-=(*it).first; if(gcd(c,(*it).second)==1) res+=(*it).second; } } printf("%lld\n",res); } else { scanf("%d%d",&x,&y); s[x]=y; } } } return 0; }