HDU 4407 12年金華網絡賽H題(容斥原理)
題意:
有一個元素為 1~n 的數列{An},有2種操作(1000次):
1、求某段區間 [a,b] 中與 p 互質的數的和。
2、將數列中某個位置元素的值改變。
分析:由於最開始是從1~n的數,之後再修改的值,所以先求解當沒有發生改變的值的和求出來,求法采用容斥原理求1~n中與m互質的數的和。先對m進行素因子分解,然後找小於n中能整除m的素因子的個數,這些數求和就是那些不互質的數的和。然後再把更改的東西加加減減就好了。不過別忘判斷是不是在x,y范圍內。(數據有可能爆int)
容斥原理詳見:容斥原理;
代碼如下:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- struct sa{
- int fen[10];
- int k;
- }p[400005];
- int a[400005];
- bool phi[400005];
- int gcd(int a,int b){
- return b==0?a:gcd(b,a%b);
- }
- long long sum;
- void dfs(int id,int w,int k,long long m,long long sumsum){
- if(id==p[k].k+1)return;
- for(int i=w;i
- long long temp=p[k].fen[i]*sumsum;
- if(id&1){
- long long t=m/temp;
- sum-=temp*(t+1)*t/2;
- }
- else {
- long long t=m/temp;
- sum+=temp*(t+1)*t/2;
- }
- dfs(id+1,i+1,k,m,temp);
- }
- }//遞歸實現容斥原理
- long long solve(long long n,int pp){
- sum=n*(n+1)/2;
- // cout<
- dfs(1,0,pp,n,1);
- return sum;
- }
- int main()
- {
- int t;
- scanf("%d",&t);
- for(int i=1;i<400005;i++)
- p[i].k=0;
- memset(phi,0,sizeof(phi));
- phi[1]=true;
- for(int i=2;i<400005;i++){
- if(!phi[i]){
- for(int j=i;j<400005;j+=i){
- phi[j]=true;
- p[j].fen[p[j].k]=i;
- p[j].k++;
- }
- }
- }//先遞推求解素因子的和
- while(t--){
- map<int,int>a;
- map<int,int>::iterator it;
- int n,m;
- scanf("%d%d",&n,&m);
- while(m--){
- int k;
- scanf("%d",&k);
- if(k==2){
- int x,c;
- scanf("%d%d",&x,&c);
- a[x]=c;
- }
- else if(k==1){
- int x,y,pp;
- long long ans;
- scanf("%d%d%d",&x,&y,&pp);
- ans=solve(y,pp)-solve(x-1,pp);
- for(it=a.begin();it!=a.end();it++){
- if(it->first>=x&&it->first<=y){//千萬別忘了這個條件
- if(gcd(it->first,pp)==1)
- ans-=it->first;
- if(gcd(it->second,pp)==1)
- ans+=it->second;
- }
- }
- printf("%I64d\n",ans);
- }
- }
- }
- return 0;
- }