ARC068C - Snuke Line
Description
其实就是给出
n
n
n个区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]对于每一个
i
∈
[
1
,
M
]
i\in[1,M]
i∈[1,M],求:
∑
j
=
1
n
[
⌊
r
j
i
⌋
−
⌊
l
j
−
1
i
⌋
≥
1
]
\sum_{j=1}^n[\lfloor \frac{r_j}{i}\rfloor-\lfloor \frac{l_j-1}{i}\rfloor\geq 1]
j=1∑n[⌊irj⌋−⌊ilj−1⌋≥1]
好吧其实这样思考感觉更难了。。。
Solution
考虑计算这个的瓶颈,我们自然地想到用调和级数级别的方法去计算这类问题:对于每个位置 t t t,求出 s t = ∑ j t ∈ [ l j , r j ] s_t=\sum_j{t\in[l_j,r_j]} st=∑jt∈[lj,rj],也就是 t t t被包含的区间个数(这可以用树状数组维护),然后直接枚举 i i i的倍数计算答案。但这样会有问题,原因在于一个区间可能存在多个 i i i的倍数,产生重复贡献。
进一步的,我们发现当 r j − l j + 1 > = i r_j-l_j+1>=i rj−lj+1>=i时,必然为 1 1 1,否则必然不大于 1 1 1,有了这个性质,我们就可以只对长度小于 i i i的区间统计 s i s_i si,枚举 i i i的倍数计算,剩下的区间都会产生 1 1 1的贡献,直接统计即可。
时间复杂度 O ( n l n M l g M + n l g n ) O(n\;lnM\;lgM+nlgn) O(nlnMlgM+nlgn)。
Code
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
using namespace std;
template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;
const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=300005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
int f=1,x=0; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
PR a[MAXN];
int s[MAXN],n,m;
void add(int x,int y) { for (;x<=m+1;x+=x&(-x)) s[x]+=y; }
int query(int x) { int ans=0; for (;x;x-=x&(-x)) ans+=s[x]; return ans; }
signed main()
{
n=read(),m=read();
for (int i=1;i<=n;i++) a[i].fi=read(),a[i].se=read();
sort(a+1,a+n+1,[&](PR x,PR y){ return x.se-x.fi<y.se-y.fi; });
for (int i=1,nw=1;i<=m;i++)
{
while (nw<=n&&a[nw].se-a[nw].fi+1<i) add(a[nw].fi,1),add(a[nw].se+1,-1),nw++;
int ans=n-nw+1;
for (int j=i;j<=m;j+=i) ans+=query(j);
printf("%d\n",ans);
}
return 0;
}