数据范围:(2<=n<=2*10^5,1<=l_i<=r_i<=10^9)
Solution
稍微想一下就大概是你确定下来(a_{i}-a_{i+1})的值(d),再确定下来开始(a_0),那么一条线就确定下来了,然后我们要满足:
[egin{aligned}
l_i&<=a_0-icdot d<=r_i\
l_i+icdot d&<=a_0<=r_i+icdot d\
end{aligned}
]
然后我们建立一个横轴表示(d),纵轴表示(a_0)的坐标系,那么(l_i+icdot d)和(r_i+icdot d)就可以看成半平面,(l_i+icdot d)取上方的,(r_i+icdot d)取下方的,然后我们预处理出所有(r_i+icdot d)交出来的一个“上凸壳”和(l_i+icdot d)交出来的一个“下凸壳”,中间围出来的部分中的整点数量就是答案(实现上的话。。因为斜率就是(i)已经排好序了所以直接求凸壳就好了)
因为是整点,所以我们可以直接算面积就好了,具体就是从左往右扫“上下凸壳”,按照交出来的交点把中间的整个部分分成若干个梯形(两边可能是三角形),然后直接用公式计算面积然后加起来就好了
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int N=2e5+10,inf=1e9;
struct Dot{
int x,y;
Dot(int x1=0,int y1=0){x=x1; y=y1;}
friend ll crs(Dot a,Dot b){return 1LL*a.x*b.y-1LL*a.y*b.x;}
friend Dot operator - (Dot a,Dot b){return Dot(a.x-b.x,a.y-b.y);}
friend double inter_pot(Dot a,Dot b){return -(1.0*a.y-b.y)/(1.0*a.x-b.x);}
ll val(ll x0){return 1LL*x*x0+y;}
}a[N*2];
Dot lst[N],rst[N];
int l[N],r[N];
int n,topl,topr;
ll ans;
int cmp(Dot x,Dot y,Dot z){
return crs(y-x,z-x)>0?1:-1;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d%d",l+i,r+i);
a[i]=Dot(i,l[i]);
a[i+n]=Dot(i,r[i]);
}
topl=0; topr=0;
for (int i=1;i<=n;++i){
while (topl>1&&cmp(lst[topl-1],lst[topl],a[i])==1) --topl;
lst[++topl]=a[i];
while (topr>1&&cmp(rst[topr-1],rst[topr],a[i+n])==-1) --topr;
rst[++topr]=a[i+n];
}
int pl=1,pr=topr;
ll edl,edr,st=-inf,ed;
while (pl<=topl&&pr){
edl=pl+1<=topl?floor(inter_pot(lst[pl],lst[pl+1])):inf;
edr=pr-1?floor(inter_pot(rst[pr],rst[pr-1])):inf;
ed=min(edl,edr);
if (lst[pl].x<rst[pr].x)
st=max(st,(ll)ceil(inter_pot(lst[pl],rst[pr])));
else if (lst[pl].x>rst[pr].x)
ed=min(ed,(ll)floor(inter_pot(lst[pl],rst[pr])));
if (st<=ed)
ans+=1LL*(ed-st+1)*((rst[pr].val(st)-lst[pl].val(st)+1)+(rst[pr].val(ed)-lst[pl].val(ed)+1))/2;
if (min(edl,edr)==edl) ++pl;
else --pr;
st=min(edl,edr)+1;
}
printf("%lld
",ans);
}