初次接触分块思想

在练习mobius反演的时候有一题需要用分块的思想来优化,于是第一次听说了分块思想。比较有名的当属号称可以解决所有不修改、离线查询问题的莫队算法。几乎所有的莫队算法的介绍都和[BZOJ]2038 小Z的袜子有关,相关大神博客:http://www.cnblogs.com/kuangbin/p/3263483.html
先来个稍简单的分块题:


codeforces 86 D. Powerful array

大意:给定一串数字,给定一系列询问,问在相应区间内 Ks·Ks·s     的和,其中Ks是K数字S在区间内出现的次数。
分块解决,相关证明:

Let's sort the query intervals according to the following rule: first come the intervals with the left ends in Q_1, then the intervals with the left ends in Q_2, and so on. And if the left ends of the two queries belong to the same part, then the interval with the more left right end is assumed to be smaller.
In order to prove the stated asymptotic behavior we will follow the steps of the left and the right ends independently. We note that the left ends that belong to the same Q_i make <= n / p steps, and transition to Q_{i+1} costs no more than 2 * n / p steps. Therefore the left ends make <= n/p * n + 2*n steps (the second summand is O(n), and it's negligible).
The right ends in the common group move only to the right, and this proves <= n * p steps (during the whole process) estimate. We will estimate the transition to the next Q_i at no more than n steps, so overall we get < = 2 * n * p steps.
Thus, the total number of steps is no more than (n / p * n + 2 * n * p). Now we choose p = sqrt(n) which proves the statement.

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=2e5+10,M=1e6+10;
int a[N],cnt[M];
struct node{
    int x,y,l,dex;
}edge[N];
int cmp(node a,node b){
    return a.l<b.l||(a.l==b.l&&a.y<b.y) ;
}
LL ans,res[N];
int L,R;
void query(int x,int y,int dex){
    if(dex){
        for(int i=L;i<x;i++){
            cnt[a[i]]--;
            ans=ans-((cnt[a[i]]<<1)+1)*a[i];
        }
        for(int i=x;i<L;i++){
            ans=ans+((cnt[a[i]]<<1)+1)*a[i];
            cnt[a[i]]++;
        }
        for(int i=R+1;i<=y;i++){
            ans=ans+((cnt[a[i]]<<1)+1)*a[i];
            cnt[a[i]]++;
        }
        for(int i=y+1;i<=R;i++){
            cnt[a[i]]--;
            ans=ans-((cnt[a[i]]<<1)+1)*a[i];
        }
    }
    else {
        for(int i=x;i<=y;i++) {
            ans=ans+((cnt[a[i]]<<1)+1)*a[i];
            cnt[a[i]]++;
        }
    }
    L=x;  R=y;
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int n,t;
    while(cin>>n>>t){
        int len=sqrt(1.0*n);
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=0;i<t;i++){
            scanf("%d%d",&edge[i].x,&edge[i].y);
            edge[i].dex=i;
            edge[i].l=(edge[i].x-1)/len;
        }
        sort(edge,edge+t,cmp);
        ans=0;
        for(int i=0;i<t;i++){
            query(edge[i].x,edge[i].y,i);
            res[edge[i].dex]=ans;
        }
        for(int i=0;i<t;i++){
            printf("%I64d\n",res[i]);
        }
    }
    return 0;
}

这是那道和mobius相关的题:

spoj  PGCD - Primes in GCD Table(mobius+分块)
大意: 在一张(a,b)的表格中,填写完整对应交点值gcd(i,j)。问填写了多少次的素数。
即求解最大公约数是素数的组合有多少个。
莫比乌斯反演:
其中d就是各种素数。求解

设 
那么最开始处理时,sum[i*pri[j]]+=sum[i];  再前缀和处理.
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N=1e7+10;
int mu[N],pri[N/10],cnt=0;
bool vis[N];
LL sum[N];
void getmu(){
    mu[1]=1;
    for(int i=2;i<N;i++){
        if(!vis[i]){
            mu[i]=-1;
            pri[cnt++]=i;
        }
        for(int j=0;j<cnt&&pri[j]*i<N;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0){
                mu[i*pri[j]]=0;
                break;
            }
            else mu[i*pri[j]]=-mu[i];
        }
    }
    for(int i=1;i<N;i++){
        if(mu[i]){
           for(int j=0;i*pri[j]<N&&j<cnt;j++){
               sum[i*pri[j]]+=mu[i];
           }
        }
    }
    for(int i=1;i<N;i++){
        sum[i]+=sum[i-1]*1LL;
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int a,b,t;
    getmu();
    cin>>t;
    while(t--){
        scanf("%d%d",&a,&b);
        if(a>b){
            a=a^b;  b=a^b;  a=a^b;
        }
        LL ans=0;
        for(int i=1;i<=a;i++){  //10/4=2;  10/2=5;
            int t1=a/i,t2=b/i;
            int next=min(a/t1,b/t2);
            ans=ans+1LL*t1*t2*(sum[next]-sum[i-1]);
            i=next;
        }
        printf("%lld\n",ans);
    }
    return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值