bzoj 3209 数论/数位DP

2 篇文章 0 订阅
2 篇文章 0 订阅

题意:定义sum(i)表示数i的二进制表示中1的个数,给定一个n,求phi(sum(i)) (1<=i<=n) 对10000007 取模的结果

一眼看过去,暴力打表找规律,然后并没有发现任何有用的事情(然而机房里的一个神犇用神一样的高端大气上档次的达标方法成功找到规律并直接拿下first blood   orz)

然后就在纸上手动模拟,只要找到1~n中,二进制表示恰好有k个1的数的个数再乘上这么多的k即可

显然用快速幂,个数作为指数,k作为底数

怎样统计呢?

组合数嘛~

c(n,m)表示n位二进制中有m个1

那么显然 c(n,m)=c(n-1,m)+c(n-1,m-1)

然后对于给定的n,转化成二进制后有l位,

枚举1的个数x(1<=x<=l)

对于每个x,从最高位到最低位判断n的二进制数的该位是否是1

如果是1,则inc(ans,c[i-1,x])并同时dec(x),然后继续

注意:(1)因为这种方法只能求出小于n的phi,所以在处理之前inc(n),这样处理出的答案就是1~n的

             (2)组合数作为指数,不需要取模 2333333

             (3)组合数的初值,从0~n的c[i,0]=1,c[1,1]=1,mdzz,就因为这里一直wa

             (4)注意没找到一个该位是1的要dec(x),不然会算到比n大的数

const
        mo=10000007;
var
        n,ans,l,tt      :int64;
        way             :array[0..70] of int64;
        i               :longint;
        c               :array[0..70,0..70] of int64;
procedure pre_do;
var
        i,j :longint;
begin
   for i:=0 to 60 do c[i,0]:=1;
   c[1,1]:=1;
   for i:=2 to 60 do
     for j:=1 to i do
       c[i,j]:=c[i-1,j-1]+c[i-1,j];
end;

function work(x:longint):int64;
var
        ans:int64;
        i:longint;
begin
   ans:=0;
   for i:=l downto 1 do
     if way[i]=1 then
     begin
        inc(ans,c[i-1,x]);
        dec(x);
        if x<0 then break;
     end;
   exit(ans);
end;

function qp(a,b:int64):int64;
var
        tt:longint;
begin
   tt:=1;
   while (b>0) do
   begin
      if b and 1=1 then tt:=tt*a mod mo;
      a:=a*a mod mo;
      b:=b>>1;
   end;
   exit(tt);
end;

begin
   read(n);
   if n=0 then
   begin
      writeln(0);exit;
   end;
   pre_do;
   l:=0; inc(n);
   while (n>0) do
   begin
      inc(l);
      way[l]:=n and 1;
      n:=n>>1;
   end;
   ans:=1;
   for i:=1 to l do
     ans:=ans*qp(i,work(i)) mod mo;
   writeln(ans);
end.
——by Eirlys


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值