1. 程式人生 > >2018.12.27-dtoj-4089-line

2018.12.27-dtoj-4089-line

題目描述:

給定 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; }
View Code