【HDU6134】Battlestation Operational(莫比乌斯反演)

记录一个菜逼的成长。。

###2017 Multi-University Training Contest - Team 8
题目链接

题目大意:
让你求 f ( n ) = ∑ i = 1 n ∑ j = 1 n ⌈ i j ⌉ [ ( i , j ) = 1 ] f(n) = \sum_{i = 1}^{n} \sum_{j = 1}^{n} \lceil \frac{i}{j} \rceil [(i, j) = 1] f(n)=i=1nj=1nji[(i,j)=1]

理解了q巨的解法后,对反演理解了点。。

以下是q巨说的解法:
g ( i ) = ∑ j = 1 n ⌈ i j ⌉ g(i) = \sum_{j = 1}^{n} \lceil \frac{i}{j} \rceil g(i)=j=1nji, h ( i ) = ∑ j = 1 n ⌈ i j ⌉ [ ( i , j ) = 1 ] h(i) = \sum_{j = 1}^{n} \lceil \frac{i}{j} \rceil [(i, j) = 1] h(i)=j=1nji[(i,j)=1]
那么 g ( i ) = ∑ d ∣ i h ( d ) g(i) = \sum_{d|i}h(d) g(i)=dih(d)
//求解 g ( i ) g(i) g(i)我是枚举 j ( 2 < = j < = N ) j(2 <= j <= N) j(2<=j<=N) ⌈ i j ⌉ \lceil \frac{i}{j} \rceil ji,然后可以知道 [ ( j − 1 ) ∗ i + 1 , j ∗ i ] [(j-1)*i+1,j*i] [(j1)i+1,ji]这段区间是可以一起处理的,因为这段区间除以 j j j后的结果都一样。
我们只需 g ( ( j − 1 ) ∗ i + 1 ) + = j , g ( j ∗ i + 1 ) − = j g((j-1)*i+1) += j,g(j*i+1) -= j g((j1)i+1)+=j,g(ji+1)=j,然后前缀和就是要求的 g ( i ) g(i) g(i)

现在我们已经知道了 g ( i ) g(i) g(i),要求 h ( i ) h(i) h(i)。我们可以用莫比乌斯反演。
可知 h ( i ) = ∑ d ∣ i μ ( d ) g ( i / d ) h(i) = \sum_{d|i}\mu(d)g(i/d) h(i)=diμ(d)g(i/d)
如果你直接两个for循环枚举 i i i i i i的因子是会T的。
这里我们第一个for可以枚举 d d d,然后第二个for枚举 i i i每次加 d d d,可以在 O ( n l o g n ) O(nlogn) O(nlogn)的时间内处理出来(应该是这个复杂度。。)
然后对 h ( i ) h(i) h(i)求前缀和就是 f ( i ) f(i) f(i)
然后 O ( 1 ) O(1) O(1)查询

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define cl(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1000000 + 10;
LL g[maxn],h[maxn],f[maxn];
bool check[maxn];
int mu[maxn],prime[maxn];
void Mobius()
{
  cl(check,false);
  mu[1] = 1;
  int tot = 0;
  for( int i = 2; i < maxn; i++ ){
    if(!check[i]){
      prime[tot++] = i;
      mu[i] = -1;
    }
    for( int j = 0; j < tot; j++ ){
      if(i * prime[j] >= maxn)break;
      check[i*prime[j]] = true;
      if(i % prime[j] == 0){
        mu[i * prime[j]] = 0;
        break;
      }
      else {
        mu[i * prime[j]] = -mu[i];
      }
    }
  }
}
void init(int N)
{
  for (int i = 2; i < N; i++) {
    for (int j = 2; (j-1)*i+1 < N; j++) {
      g[(j-1)*i+1] += j;
      g[min(N,j*i+1)] -= j;
    }
  }
  for( int i = 1; i < N; i++ ){
    g[i] += g[i-1];
    g[i] %= MOD;
  }
  for( int i = 1; i < N; i++ ){
    g[i] += (i != 1 ? i+1 : i);
    g[i] %= MOD;
  }
  for( int j = 1; j < N; j++ ){
    for( int i = j; i < N; i += j ) {
      h[i] = (h[i] + mu[j] * g[i/j]) %  MOD;
    } 
  }
  for( int i = 1; i < N; i++ )
    f[i] = (f[i-1] + h[i]) % MOD;
}
int main()
{
  //fin,fout;
  Mobius();
  init(1000001);
  int n;
  while(~scanf("%d",&n)){
    printf("%lld\n",f[n]);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值