莫比乌斯反演

    莫比乌斯反演就是说得通俗点就是把元素的和通过加上系数还原成原来的元素,其中最主要的就是确定系数,即莫比乌斯函数(广义)。

    在信息学中,使用数论中的莫比乌斯反演最好是用线性筛法O(n)和分块求和O(sqrt(n)).

    题目:spoj7001(入门1)

program spoj7001;
var
  N:longint;
  tp:array[1..1000000] of boolean;
  prime,s,u:array[0..1000000] of int64;
  pn,i,j,t:longint;
  ans,now:int64;

procedure pre;
var
  i,j:longint;
begin
  fillchar(tp,sizeof(tp),0);
  for i:=2 to 1000000 do begin
    if not tp[i] then begin
      inc(pn);prime[pn]:=i;u[i]:=-1;
    end;
    for j:=1 to pn do begin
      if i*prime[j]>1000000 then break;
      tp[prime[j]*i]:=true;
      if i mod prime[j]=0 then begin
        u[i*prime[j]]:=0;break;
      end;
      u[i*prime[j]]:=-u[i];
    end;
  end;
  u[1]:=1;s[0]:=0;
  for i:=1 to 1000000 do s[i]:=s[i-1]+u[i];
end;



begin
  pre;
  readln(T);
   for j:=1 to T do begin
    readln(N);
    i:=1;ans:=0;
    while i<=N do begin
      now:=N div (N div i);
      ans:=ans+((N div now+1)*(N div now+1)*(N div now+1)-1)*(s[now]-s[i-1]);
      i:=now+1;
    end;
    writeln(ans);
   end;
end.

ZOJ3435(入门2)

program zoj3435;
var
  u,sum:array[0..1000000] of int64;
  prime:array[0..1000000] of int64;
  tp:array[0..1000000] of boolean;
  ans,L,W,H,now,i:int64;
  pn:Longint;

procedure getprime;
var
  j,i:longint;
  flag:boolean;
begin
  fillchar(tp,sizeof(tp),0);
  pn:=0;u[1]:=1;
  for i:=2 to 1000000 do begin
    if not tp[i] then begin
      inc(pn);prime[pn]:=i;u[i]:=-1;
    end;
    for j:=1 to pn do begin
      if prime[j]*int64(i)>1000000 then break;
      tp[prime[j]*i]:=true;
      if i mod prime[j]=0 then begin
        u[prime[j]*i]:=0;break;
      end;
      u[prime[j]*i]:=-u[i];
    end;
  end;
  sum[1]:=1;
  for i:=2 to 1000000 do sum[i]:=sum[i-1]+u[i];
end;

function max(a,b:longint):longint;begin if a>b then exit(a);exit(b);end;
function min(a,b:longint):longint;begin if a<b then exit(a);exit(b);end;

begin
  getprime;
  while not seekeof do begin
    readln(L,W,H);
    dec(L);dec(W);dec(H);
    ans:=0;
    i:=1;
    sum[0]:=0;
    while (i<=L) or (i<=W) or (i<=H) do begin
      now:=maxlongint;
      if i<=L then now:=min(now,L div (L div i));
      if i<=W then now:=min(now,W div (W div i));
      if i<=H then now:=min(now,H div (H div i));
      ans:=ans+(sum[now]-sum[i-1])*((L div now+1)*(W div now+1)*(H div now+1)-1);
      i:=now+1;
    end;
    writeln(ans);
  end;
end.

spoj4491(精通)

这道题不能套模板,需要灵活将miu修改。(为了节约内存,miu直接用prime代替)

program spoj4491;
const
  maxn=10000000;
var
  sum:array[0..10000000] of int64;
  cn1,cn2:array[0..10000000] of longint;
  prime:array[0..10000000] of int64;
  tprime:array[1..10000000] of boolean;
  pn:longint;
  n,i:longint;
  x,y:int64;

procedure pre;
var
  i,j:longint;
begin
  fillchar(tprime,sizeof(tprime),0);
  pn:=0;
  for i:=2 to maxn do begin
    if not tprime[i] then begin
      inc(pn);prime[pn]:=i;
      cn1[i]:=1;cn2[i]:=0;
    end;
    for j:=1 to pn do begin
      if prime[j]>maxn div i then break;
      tprime[prime[j]*i]:=true;
      if i mod prime[j]=0 then begin
        if i div prime[j] mod prime[j]=0 then begin
          cn2[i*prime[j]]:=2;
          cn1[i*prime[j]]:=cn1[i];
        end
        else begin
          cn1[i*prime[j]]:=cn1[i];
          cn2[i*prime[j]]:=cn2[i]+1;
        end;
        break;
      end;
      cn1[i*prime[j]]:=cn1[i]+1;
      cn2[i*prime[j]]:=cn2[i];
    end;
  end;
  prime[1]:=0;
  for i:=2 to maxn do
    if cn2[i]=1 then begin
      prime[i]:=-(cn1[i] and 1)shl 1+1;
    end
    else if cn2[i]=0 then begin
       prime[i]:=cn1[i]*((cn1[i] and 1) shl 1-1)
    end
    else prime[i]:=0;
  for i:=1 to maxn do sum[i]:=sum[i-1]+prime[i];
end;

procedure work;
var
  i,now,j:longint;
  ans:int64;
function min(a,b:longint):longint;begin if a<b then exit(a);exit(b);end;
begin
  ans:=0;
   i:=1;
   while (i<=x) or (i<=y) do begin
    now:=maxlongint;
    if i<=x then now:=min(now,x div (x div i));
    if i<=y then now:=min(now,y div (y div i));
    ans:=ans+(x div now)*(y div now)*(sum[now]-sum[i-1]);
    i:=now+1;
   end;
  writeln(ans);
end;

begin
  pre;
  readln(n);
  for i:=1 to n do begin
    readln(x,y);
    work;
  end;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值