[SDOI2014]数表 解题报告

11 篇文章 0 订阅

这题并没有做出来。。
f(i)=d|id ,考虑按f(i)排序,则对于询问 (n,m),(nm) ,f(i)会贡献 ndd=1μ(d)nidmid ,这样我们按照id考虑即可。。
但是我做的时候错误地把它化成了这样:
ndd=1μ(d)nidmid
这样就很难考虑出来该怎么做了。。
代码:

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
struct QS{
    int n,m,a,i;
    bool operator < (const QS & o)const{
        return a<o.a;
    }
}q[20005];
int ans[20005];
int bit[100005];
const int S=400;
int f[405];
int N;
void update(int x,int delta){
    for(;x<=N;x+=x&-x)bit[x]+=delta;
}
int query(int x){
    int ans=0;
    for(;x;x-=x&-x)ans+=bit[x];
    return ans;
}
int mu[100005],prime[10005],smp[100005];
pair<int,int> d[100005];
int main(){
    freopen("bzoj_3529.in","r",stdin);
    //freopen("bzoj_3529.out","w",stdout);
    int Q,i;
    scanf("%d",&Q);
    for(i=Q;i--;){
        scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a);
        if(q[i].n>q[i].m)swap(q[i].n,q[i].m);
        N=max(N,q[i].m);
        q[i].i=i;
    }
    sort(q,q+Q);
    int j;
    mu[1]=smp[1]=1;
    d[1]=make_pair(1,1);
    for(i=2;i<=N;++i){
        d[i].second=i;
        if(smp[i]==0){
            prime[++prime[0]]=i;
            d[i].first=smp[i]=i+1;
            mu[i]=-1;
        }
        for(j=1;j<=prime[0]&&i*prime[j]<=N;++j)
            if(i%prime[j]){
                mu[i*prime[j]]=-mu[i];
                smp[i*prime[j]]=prime[j]+1;
                d[i*prime[j]].first=d[i].first*smp[i*prime[j]];
                //printf("%d(%d)*%d->%d(%d)\n",d[i],i,prime[j]+1,d[i*prime[j]],i*prime[j]);
            }
            else{
                smp[i*prime[j]]=smp[i]*prime[j]+1;
                d[i*prime[j]].first=d[i].first/smp[i]*smp[i*prime[j]];
                //printf("%d(%d)/%d*%d->%d(%d)\n",d[i],i,smp[i],smp[i*prime[j]],d[i*prime[j]],i*prime[j]);
            }
    }
    //for(i=1;i<=10;++i)printf("%d:%d %d\n",i,d[i],smp[i]);
    sort(d+1,d+N+1);
    int k,o=0;
    q[Q].a=0x7fffffff,d[N+1].first=0x7fffffff;
    for(i=1;i<=N;){
        do{
            for(j=d[i].second,k=1;j<=min(S,N);++k,j+=d[i].second)
                if(mu[k]){
                    //cout<<"update:"<<j<<endl;
                    f[j]+=mu[k]*d[i].first;
                }
            for(;j<=N;++k,j+=d[i].second)
                if(mu[k])
                    update(j,mu[k]*d[i].first);
            //cout<<"Add:"<<d[i].first<<","<<d[i].second<<endl;
            ++i;
        }while(d[i].first==d[i-1].first);
        for(;q[o].a<d[i].first;++o){
            for(j=1;j<=min(S,q[o].n);++j){
                //cout<<"Q["<<j<<"]:"<<f[j]<<"\n";
                ans[q[o].i]+=(q[o].n/j)*(q[o].m/j)*f[j];
            }
            for(;j<=q[o].n;j=k+1){
                k=min(q[o].n/(q[o].n/j),q[o].m/(q[o].m/j));
                //cout<<"Q["<<j<<","<<k<<"]\n";
                ans[q[o].i]+=(q[o].n/j)*(q[o].m/j)*(query(k)-query(j-1));
            }
        }
    }
    for(i=Q;i--;)printf("%d\n",ans[i]&0x7fffffff);
}

总结:一定要注意[[n/a]/b]=[n/ab]。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值