ARC068C - Snuke Line

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=1n[irjilj11]
好吧其实这样思考感觉更难了。。。

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 rjlj+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值