HDU 4407 sum 容斥原理

算法:

利用数据1...N的性质,求与P的互质的个数,位运算,容斥定理。。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<string>
#include<math.h>
#include<map>
#include<set>
#include<algorithm>
using namespace std;

struct info
{
  int s,e,p,num;
}seg[1010];

struct pinfo
{
  int id, v, num;   
}p[1010];

int factor[1000]; //保存因子 
map<int,int>mp;

int gcd( int n, int m )
{
  return m ? gcd(m, n % m ) : n;    
}
 
//容斥定理求互质之和 
long long cal( int x, int n)
{
  //分解出x的因子
  if( x == 0 )
    return 0;
  int i,Lim = sqrt( x * 1.0 ), cnt = 0;
  for( i = 2; i <= Lim; i++)
  {
     if( x % i == 0 )
     {
         factor[++cnt] = i;
         while( x % i == 0 )
            x = x / i;   
     }        
        
  }
  if( x != 1 )
  {
     factor[++cnt] = x;      
  }
  int mask = 1 << cnt;
  long long ans = 0;
  for( int i = 1; i < mask; i++)
  {  
     int tt = 0, fac = 1, k;
     for( int j = 0; j < cnt; j++)
     {
        if( i & (1<<j) )
        {
           tt++;
           fac *=  factor[j+1];
        }       
     }
     //如果是奇数项      
     if( tt & 1 )
     {
        k = n / fac;
        ans += ( fac + 1LL * fac * k ) * k / 2;
     }   
     else
     {
        k = n / fac;
        ans -= ( fac + 1LL * fac * k ) * k / 2;  
     } 
  }     
  return ans;   
}

int main( )
{
  int N, M, a, b, c, d, c1, c2, T;
  scanf("%d",&T);
  while( T-- )
  {  
     scanf("%d%d",&N,&M);
     c1 = c2 = 0;
     for( int i = 1; i <= M; i++)
     {
        scanf("%d",&a);
        if( a == 1 )
        {
           scanf("%d%d%d",&seg[c1].s, &seg[c1].e, &seg[c1].p);
           seg[c1].num = i;       
           c1++;
        }
        else
        {
           scanf("%d%d",&p[c2].id, &p[c2].v);
           p[c2].num = i;
           c2++; 
        }              
     } 
     long long x, y, pans;
     for( int i = 0; i < c1; i++) //枚举每一个查询 
     {
        x = cal(seg[i].p, seg[i].s-1);
        y = cal(seg[i].p, seg[i].e);
        pans = ((1 + seg[i].e * 1LL) * seg[i].e * 1LL / 2 - y ) - (1 + seg[i].s - 1 ) * 1LL * (seg[i].s - 1) * 1LL / 2 + x;
        mp.clear();
        for( int j = 0; j < c2; j++)
        {
            if( p[j].num <  seg[i].num && seg[i].e >= p[j].id && seg[i].s <= p[j].id )
            {
              mp[p[j].id] = p[j].v;   
            }
            else if( p[j].num > seg[i].num )
              break;    
        }
        map<int,int>::iterator it;
        for( it = mp.begin(); it != mp.end(); it++)
        {
            int xx = gcd(seg[i].p, it->second);
            int yy = gcd(seg[i].p, it->first);
            if( yy == 1 && xx != 1 )
                pans -= it->first;
            else if( yy != 1 && xx == 1 )
                pans += it->second;      
            else if( yy == 1 && xx == 1 )
                pans += it->second - it->first;  
        } 
        printf("%I64d\n", pans); 
     }
       
  }
  return 0;
}

 

转载于:https://www.cnblogs.com/tangcong/archive/2012/09/22/2698434.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值