AGC 068 E Snuke Line 解题报告 (树状数组)
题意
有一趟列车有 \(M+1\) 个车站, 从 \(0\) 到 \(M\) 编号.
有 \(N\) 种商品, 第 i 种只在编号 \([l_i,r_i]\) 的车站出售.
一辆列车有一个预设好的系数 \(d\), 从 \(0\) 出发, 只会在 \(d\) 的倍数车站停车.
对于 \(d\) 从 \(1\) 到 \(M\) 的列车, 求最多能买到多少种商品.
复制粘贴
思路
首先明确一点, 如果枚举每辆列车所经过的每个车站的话, 复杂度是 \(O(n\log n)\) 的.
因为总次数是 \(\sum_{i=1}^{n} \frac{n}{i}\), 而这货 \(\sum_{i=1}^{n} \frac{1}{i}\) 是 \(\log n\) 级别的 (这是一个叫调和数列的东西, 但是我不会), 所以总复杂度就是 \(O(n\log n)\).
但是, 如果我们只是单纯地把每种商品加到它对应的区间的话, 会导致重复计算.
而我们发现, 当 \(r_i-l_i+1 \ge d\) 时, 这中商品必然能够被买到, 而 \(r_i-l_i+1 < d\) 一定不会被重复计算,
所以, 我们只需要把 \(r_i-l_i+1 < d\) 的商品加到它对应的区间就行了,
我们的操作是区间修改, 单点查询, 用 树状数组 + 差分 就行了.
所以, 从小到大枚举 \(d\) 每次把 \(r_i-l_i+1 < d\) 的商品扔进树状数组, 然后对每个车站查询就好了.
代码
#include<bits/stdc++.h>
using namespace std;
const int _=1e5+7;
const int __=3e5+7;
struct com{
int l,r,len;
}c[__];
int n,m,tr[_],ans;
bool rule(com a,com b){ return a.len<b.len; }
void add(int x,int w){
for(int i=x;i<=m;i+=i&(-i))
tr[i]+=w;
}
void modify(int l,int r,int w){
add(l,w);
add(r+1,-w);
}
int query(int x){
int res=0;
for(int i=x;i;i-=i&(-i))
res+=tr[i];
return res;
}
int main(){
//#ifndef ONLINE_JUDGE
//freopen("x.in","r",stdin);
//#endif
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d%d",&c[i].l,&c[i].r);
c[i].len=c[i].r-c[i].l+1;
}
sort(c+1,c+1+n,rule);
int p=1;
for(int i=1;i<=m;i++){
while(p<=n&&c[p].len<i){
modify(c[p].l,c[p].r,1);
p++;
}
//printf("p: %d\n",p);
ans=n-p+1;
for(int j=i;j<=m;j+=i)
ans+=query(j);
printf("%d\n",ans);
}
return 0;
}