2018.12.27-dtoj-4089-line
阿新 • • 發佈:2018-12-28
題目描述:
給定 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=0View Code;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; }