【做题】arc068_e-Snuke Line——利用特殊性质分讨

时间:2022-01-09 20:46:24

显然,对于所有跨度暴力扫一遍的复杂度本身只有\(O(n \log n)\)。

容易想到在每一个到达的位置加上覆盖这个位置的区间数。剩下的问题就在于如何处理覆盖了多个位置的区间。

记录已访问或去重都是难以实现的。但注意到一个性质:

所有长度不小于跨度的区间一定都能被访问到,所有长度小于跨度的区间至多访问一次。

于是我们枚举跨度,维护长度不小于当前跨度的区间数,把长度小于当前跨度的区间加入到一个支持区间加和单点查询的数据结构里就可以了。

时间复杂度\(O(n \log ^2 n)\)。

#include <bits/stdc++.h>
using namespace std;
const int N = 300010, M = 100010;
#define lowbit(x) ((x) & (-(x)))
int v[M],n,m,s,ans,p=1;
struct data {
int l,r;
bool operator < (const data& x) const {
return r - l < x.r - x.l;
}
} dat[N];
void add(int p,int val) {
for (; p <= s ; p += lowbit(p))
v[p] += val;
}
int query(int p) {
int res = 0;
for (; p ; p -= lowbit(p))
res += v[p];
return res;
}
int main() {
scanf("%d%d",&n,&m);
for (int i = 1 ; i <= n ; ++ i)
scanf("%d%d",&dat[i].l,&dat[i].r);
sort(dat+1,dat+n+1);
s = m+1;
for (int i = 1 ; i <= m ; ++ i) {
while (dat[p].r - dat[p].l + 1 < i && p <= n) {
add(dat[p].l+1,1);
add(dat[p].r+2,-1);
p ++;
}
ans = n - p + 1;
for (int j = 0 ; j <= m ; j += i)
ans += query(j+1);
printf("%d\n",ans);
}
return 0;
}

小结:利用题目特殊性质分类讨论可以有效简化题目。