[arc068e]Snuke Line

题目大意

有n个区间,现在询问你对于1<=i<=m的每个i,有多少个区间至少包含一个i的倍数?

做法

正难则反,如果一个区间不包含i的倍数,一定是被相邻两个i的倍数夹着或者在最大的i的倍数的右边。
调和级数的时间把点对弄出来,然后不难想到离线+树状数组。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxm=100000+10,maxn=300000+10;
int tree[maxm],ans[maxm];
int h[maxm],go[maxm*20],nxt[maxm*20],ask[maxm*20][3];
int h2[maxm],g2[maxn],n2[maxn];
int i,j,k,l,t,n,m,tot,top;
void add(int x,int y){
    go[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y){
    g2[++tot]=y;
    n2[tot]=h2[x];
    h2[x]=tot;
}
int lowbit(int x){
    return x&-x;
}
void change(int x){
    while (x){
        tree[x]++;
        x-=lowbit(x);
    }
}
int query(int x){
    int t=0;
    while (x<=m){
        t+=tree[x];
        x+=lowbit(x);
    }
    return t;
}
int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        scanf("%d%d",&j,&k);
        add2(k,j);
    }
    tot=0;
    fo(i,1,m) ans[i]=n;
    fo(i,1,m){
        fo(j,1,m/i){
            ask[++top][0]=i*(j-1);
            ask[top][1]=i*j;
            ask[top][2]=i;
            add(ask[top][1],top);
        }
        top++;
        ask[top][0]=(m/i)*i;
        ask[top][1]=m+1;
        ask[top][2]=i;
        add(m+1,top);
    }
    fo(i,0,m+1){
        t=h[i];
        while (t){
            j=go[t];
            ans[ask[j][2]]-=query(ask[j][0]+1);
            t=nxt[t];
        }
        t=h2[i];
        while (t){
            change(g2[t]);
            t=n2[t];
        }
    }
    fo(i,1,m) printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值