标签:分享 char play define namespace ons none 图片 lib
给定 n 个?元组 [li.ri] ,求有多少整数序列 a 满?∀1 ≤ i ≤ n,li ≤ ai ≤ ri 且 {(i,ai)|1 ≤ i ≤ n} 在平?上形成了?条直线。
容易得到式子,li<=a1+(i-1)*d<=ri,把两个式子拆开看成为一个拥有2*n条直线的半平面交,于是我们的任务变成求围成的凸多边形内所拥有的整点个数。
考虑这是一个由至多2*n条直线构成的凸多边形,上下范围在某个区间内被相同两条直线所限制,在这段区间内所能形成的整点数,是一个等比数列可以o1求出,于是只有处理出区间,就可以得到整点个数,效率O(n)。
注意!!因为d的范围可以达到负数,所以在转式子的过程中,正负影响不等式符号,导致我们要分两半计算。
#include<bits/stdc++.h> #define il inline #define LL long long #define db double #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=2e5+5,inf=1e9; int n,l[N],r[N],mn=inf,mx,q1[N],tot1,q2[N],tot2;LL ans=0;db j[N],j2[N]; il int read(){int x;char ch;_(!);x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return x;} il db gj(db k1,db b1,db k2,db b2){return (b1-b2)/(k2-k1);} il LL gs(int L,int len,int d){return (LL)L*len+(LL)len*(len-1)/2*d;} il void work(){ tot1=tot2=0; for(int i=n;i;i--){ while(tot1>1&&gj(-i,l[i],-q1[tot1-1],l[q1[tot1-1]])<j[tot1])tot1--; j[1+tot1]=gj(-i,l[i],-q1[tot1],l[q1[tot1]]);q1[++tot1]=i; } for(int i=1;i<=n;i++){ while(tot2>1&&gj(-i,r[i],-q2[tot2-1],r[q2[tot2-1]])<j2[tot2])tot2--; j2[tot2+1]=gj(-i,r[i],-q2[tot2],r[q2[tot2]]);q2[++tot2]=i; } j[tot1+1]=j2[tot2+1]=inf;int t1=1,t2=1; for(int la=1;;){ while(t1<tot1&&j[t1+1]<la)t1++;while(t2<tot2&&j2[t2+1]<la)t2++; db p=(q1[t1]!=q2[t2]?gj(-q1[t1],l[q1[t1]],-q2[t2],r[q2[t2]]):inf); int R=min(min((int)j[t1+1],(int)j2[t2+1]),(p<la?inf:(int)p)); if(-la*q2[t2]+r[q2[t2]]+la*q1[t1]-l[q1[t1]]+1<=0){ if(q1[t1]<q2[t2])break; la=(p<la?R:(p-(int)p!=0?p+1:(int)p));continue; } ans+=gs(-la*q2[t2]+r[q2[t2]]-l[q1[t1]]+la*q1[t1]+1,R-la+1,-(q2[t2]-q1[t1])); la=R+1; } } int main() { n=read(); for(int i=1;i<=n;i++)l[i]=read(),r[i]=read(),mn=min(mn,r[i]),mx=max(mx,l[i]); ans=max(0,mn-mx+1);work(); for(int i=1;i<=n;i++){int t=l[i];l[i]=-r[i];r[i]=-t;} work();printf("%lld\n",ans); return 0; }
标签:分享 char play define namespace ons none 图片 lib
原文地址:https://www.cnblogs.com/Jessie-/p/10188229.html