[arc086e]snuke line

题意:

有n个区间,询问对于$1\leq i\leq m$的每个i,有多少个区间至少包含一个i的倍数?

$1\leq N\leq 3\times 10^5$

$1\leq M\leq 10^5$

题解:

开始就想到了调和级数的复杂度,但是一直没想到反着统计。。。

正着统计区间是否包含$i$的倍数很麻烦,不妨反过来统计是否不包含,那么不包含的情况肯定是区间卡在两个$i$的倍数之间或者在$\lfloor\frac{m}{i}\rfloor\times i$和$M$之间,用调和级数的时间复杂度可以把所有倍数搞出来。那么可以将询问离线,按照$i$的倍数分段,然后用树状数组维护即可。

调和级数一个$log$,树状数组一个$log$,所以最后的时间复杂度是$O(mlogmlogn)$

貌似有很多dalao用不同姿势的分块碾了过去QAQ

代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<vector>
 5 #include<cmath>
 6 #define lb(x) (x&-x)
 7 using namespace std;
 8 struct task{
 9     int x,id;
10 }a[2000001];
11 int n,m,l,r,tot=0,ans[2000001],t[2000001];
12 vector<int>g1[2000001],g2[2000001];
13 void add(int x,int v){
14     for(;x;x-=lb(x)){
15         t[x]+=v;
16     }
17 }
18 int query(int x){
19     int ret=0;
20     for(;x<=m;x+=lb(x)){
21         ret+=t[x];
22     }
23     return ret;
24 }
25 int main(){
26     scanf("%d%d",&n,&m);
27     for(int i=1;i<=n;i++){
28         scanf("%d%d",&l,&r);
29         g1[r].push_back(l);
30     }
31     for(int i=1;i<=m;i++){
32         ans[i]=n;
33         for(int j=1;j<=m/i;j++){
34             a[++tot].x=i*(j-1);
35             a[tot].id=i;
36             g2[i*j].push_back(tot);
37         }
38         a[++tot].x=(m/i)*i;
39         a[tot].id=i;
40         g2[m+1].push_back(tot);
41     }
42     for(int i=0;i<=m+1;i++){
43         for(int j=0,jj=g2[i].size();j<jj;j++){
44             ans[a[g2[i][j]].id]-=query(a[g2[i][j]].x+1);
45         }
46         for(int j=0,jj=g1[i].size();j<jj;j++){
47             add(g1[i][j],1);
48         }
49     }
50     for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
51     return 0;
52 }
posted @ 2018-08-21 23:24  DCDCBigBig  阅读(305)  评论(0编辑  收藏  举报