2016.08.06计算几何总结测试day1
T1 bzoj1132[POI2008]TRO
還是太弱了。。。。測試時看到這題直接懵逼,極角排序什么的根本想不起來,只會n^3暴力怎么破。。。。。。不過竟然有84。。。。。QAQ
正解是n^2logn的,首先為了避免算重,以點的x坐標為第一關鍵字和y坐標為第二關鍵字排好序,然后O(n)枚舉當前點計算以當前點為三角形的一個頂點的三角形面積之和。
顯然不能n^2枚舉,于是想到nlogn極角排序,以當前點為原點建一個平面直角坐標系,加一個前綴和將計算優化到O(n),于是就是n^2logn的了
至于怎么前綴和優化,因為一個點j,向量ij與前面所有的向量的叉積之和就是向量ij的貢獻。
然后稍微手推一下就可以得出ans+=sumx*a[j].y-sumy*a[j].x就可以O(n)了。。。。。
精度問題什么的考慮一下就好了。。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 10000 int n,tot;
long long sumx,sumy,ans; struct point{
int x,y;
double slope;
}a[maxn],b[maxn]; bool cmp(point a,point b){
return a.x<b.x||(a.x==b.x&&a.y<b.y);
} bool cmp_slope(point a,point b){
return a.slope<b.slope;
} int main(){
scanf("%d",&n);
for (int i=;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a+,a+n+,cmp);
for (int i=;i<=n;i++){
tot=,sumx=,sumy=;
for (int j=i+;j<=n;j++) b[++tot].slope=atan2(a[j].y-a[i].y,a[j].x-a[i].x),b[tot].x=a[j].x-a[i].x,b[tot].y=a[j].y-a[i].y;
sort(b+,b+tot+,cmp_slope);
for (int j=;j<=tot;j++){
ans+=sumx*b[j].y-sumy*b[j].x;
sumx+=b[j].x,sumy+=b[j].y;
}
}
printf("%lld.%d",ans>>,(ans&)?:);
return ;
}
T1
T2 bzoj1038[ZJOI2008]瞭望塔
測試時看到這題認為最可寫,于是碼了1h+雜技寫法,以為用一個單調棧維護斜率什么的搞一搞就行了,然而并沒有看到瞭望塔能建在[x1,xn]的任意位置。。。。。
然后發現這就是一道半平面交的題,對每個拐點求出它左右最遠能望到哪個點,然后連兩條直線,對于所有在這條直線上方的點,它都可以看到,由于光路可逆原理,直線上方任意的點也能看到它。
然后這就是許多個半平面,求半平面交就好了。。。
然而測試時傻叉的以為半平面內y最小的點一定最優。。。。。于是狗帶。。。。竟然還有80。。。
正解是半平面交的下凸殼可以看成分段一次函數,折線下方村落的折線也可以看成分段一次函數,然后求兩段一次函數之間的最小距離,必定會在端點上而不會是直線上的某一點的距離。
因為折線可以分解為一段一段的直線,對于每一段直線,它所對應的直線斜率無論是比它大還是小,最短距離都取決于端點,平行也是一樣,YY一下就會覺得它很正確。。。很顯然。。。
于是上半平面交。。。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 330
const double inf=1e10; int n,tot,sum,head,tail;
double ans; struct point{
double x,y;
}p[*maxn],a[*maxn],pa[*maxn]; struct line{
point from,to;
double slope;
}l[*maxn],q[*maxn]; point operator -(point a,point b){return(point){a.x-b.x,a.y-b.y};}
double operator *(point a,point b){return a.x*b.y-a.y*b.x;}
bool operator <(line a,line b){
return (a.slope==b.slope)?((a.to-a.from)*(b.to-a.from)<):a.slope<b.slope;
}
bool operator ==(line a,line b){
return a.slope==b.slope;
} point getpoint(line a,line b){
double t1=(a.to-a.from)*(b.to-a.from),t2=(b.from-a.from)*(a.to-a.from);
double t=t1/(t1+t2);
return (point){b.to.x+t*(b.from.x-b.to.x),b.to.y+t*(b.from.y-b.to.y)};
} bool check(line a,line b,line c){
point d=getpoint(a,b);
return ((c.to-c.from)*(d-c.from)<);
} double find1(double x){
for (int i=;i<=n;i++)
if (x>=a[i].x && x<a[i+].x){
line tmp1,tmp2;
tmp1.from=(point){x,},tmp1.to=(point){x,max(a[i].y,a[i+].y)};
tmp2.from=(point){a[i].x,a[i].y},tmp2.to=(point){a[i+].x,a[i+].y};
point ttt=getpoint(tmp1,tmp2);
return ttt.y;
}
return a[n].y;
} double find2(double x){
for (int i=head;i<=tail;i++)
if (x>=q[i].from.x && x<=q[i].to.x){
line tmp;
tmp.from=(point){x,},tmp.to=(point){x,max(q[i].from.y,q[i].to.y)};
point ttt=getpoint(tmp,q[i]);
return ttt.y;
}
} void solve(){
head=,tail=;
q[]=l[],q[]=l[];
for (int i=;i<=sum;i++){
while (head<tail && check(q[tail-],q[tail],l[i])) tail--;
while (head<tail && check(q[head+],q[head],l[i])) head++;
q[++tail]=l[i];
}
while (head<tail && check(q[tail-],q[tail],q[head])) tail--;
while (head<tail && check(q[head+],q[head],q[tail])) head++;
q[tail+]=q[head];
for (int i=head;i<=tail;i++){
point t=getpoint(q[i],q[i+]);
q[i].to=q[i+].from=t;
ans=min(ans,fabs(t.y-find1(t.x)));
}
for (int i=;i<=n;i++)
ans=min(ans,fabs(a[i].y-find2(a[i].x)));
printf("%.3f",ans);
} int main(){
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
scanf("%d",&n);
ans=inf,inf;
for (int i=;i<=n;i++)
scanf("%lf",&a[i].x);
for (int i=;i<=n;i++)
scanf("%lf",&a[i].y);
l[++tot].from=(point){-inf,-inf},l[tot].to=(point){inf,-inf};
l[++tot].from=(point){inf,inf},l[tot].to=(point){-inf,inf};
for (int i=;i<=n;i++){
int k=;
for (int j=i+;j<=n;j++){
if (a[j].y<=a[i].y) continue;
if (!k) k=j;
else if ((a[k]-a[i])*(a[j]-a[i])>) k=j;
}
if (k) l[++tot].from=a[i],l[tot].to=a[k];
}
for (int i=n;i;i--){
int k=;
for (int j=i-;j;j--){
if (a[j].y<=a[i].y) continue;
if (!k) k=j;
else if ((a[k]-a[i])*(a[j]-a[i])<) k=j;
}
if (k) l[++tot].to=a[i],l[tot].from=a[k];
}
l[++tot].from=(point){a[].x,inf},l[tot].to=(point){a[].x,-inf};
l[++tot].from=(point){a[n].x,-inf},l[tot].to=(point){a[n].x,inf};
for (int i=;i<=tot;i++) l[i].slope=atan2(l[i].to.y-l[i].from.y,l[i].to.x-l[i].from.x);
sort(l+,l+tot+);
sum=unique(l+,l+tot+)-l;
sum--;
solve();
return ;
}
T2
T3 bzoj4677 network
zy加題果然神速。。。。。
測試時由于T2打太久了,而且樣例都還沒過。。。。于是情急之下5分鐘打了一個T3 n^2暴力,發現過了樣例開心得不得了,然而手抽多打一個0,MLE。。。。。。。QAQ
這是一個類似楊輝三角的轉移過程,可以發現,對于f[x][y],能夠轉移的p[]的區間是[y-x+1,y],含義是第只有第(y-x+1)種運輸線到第y種運輸線可能在轉移過程中出現。
發現這是一個區間的問題,于是就立馬能想到線段樹。。。然后開開心心地找到區間[y-x+1,y]的最小值,然后轉移,開開心心submit,然后WA。。。。。
因為對于最小值轉移正確性無法保證,因為你只能保證p[k]最小,不能保證sigma(p[k+1],p[y])+p[k]最小。
于是考慮最優解的形式,因為一旦起點確定,你要往下走多少次,往右走多少次也確定了,因為對于每一條運輸線都要往下走,你可以選擇的是豎直向下,也可以選擇斜向下,而往斜要走多少次已經確定了,為什么不在這條路徑上(注意,是這條路徑上)走最多次最小的k呢?
那么對于任取一起點k的最優解,一定就是如下圖所示的情況
那么既然要從某k走到權值最優的k,那么為什么不直接以權值最優的k為起點呢?那么新的情況就如下圖
這樣一來,無論如何對于這條路徑也無法優化了,那么這一定就是最優解的形式。
因為如果要每次走到最優的k再不斷往下走,不如選最優的k為起點,然而這也僅僅只是最優解的形式,并不能直接選取它作為最優解,而最優解的形式已經確定下來了,這就為我們解題帶來了很大方便。
考慮對于每一個區間內的k,都令它直接往下走,走到不能走為止,再往左。
因為如果這個k它本身的權值是不夠優的,還讓它一直往下走,顯然這不是以該k為起點的路徑的最優解,但實際上選它作為起點的路徑本就是沒有意義的,因為完全可以以該路徑上最優的點作為起點,因此即使該方案不夠優,卻不會影響到答案,因為已經確定了解的形式,且該決策本就是沒有意義的。
但如果這個k足夠優,那么顯然如果讓它一直往下走,它的決策不會比其他的方案差,所以對于能影響到答案的決策,它是最優的。
如果還是不能理解,換句話說,決策序列其實是非降的,因為對于k1<k2,還有p[k1]>p[k2],則k1一定沒有k2優,而在決策序列元素l[i],l[i+1],對于它們在原來的p[]序列中中間夾的所有數p[j]都一定有p[j]>l[i] && p[j]>l[i+1](如果它能成為一個決策,那么就一定會有這樣的一個性質),也就是說,如果用決策序列將原來的p[]序列分成若干塊,塊內的元素都是單調不升的。
而且每個塊最后一個元素都要優于塊內其余所有的元素。若對于每個元素的最優解顯然是把它的路徑先伸展到該塊最后一個元素,但它們是一定沒有該塊最后一個元素優的,所以一路下來又有何妨。同樣,每塊最后一個元素無論是最優路徑還是最優解形式的路徑都是一樣的,這也是能夠更新答案的路徑,它們的解都是最優的。
那么對于一個k,最優解形式就是k出現(x-y+k)次,其余[k+1,y]只出現一次。
也就是求一個min{(x-y+k)*p[k]+sigma(p[i]|(k+1<=i<=y))}
令sum[i]表示sigma(p[i]),那么就有相當于min{(x-y)*p[k]+k*p[k]-sum[k]}+sum[y]
令k=(x-y),x=p[k],y=k*p[k],即求kx+y。令kx+y=b,轉化為直線令截距最小,即是用線段樹維護一個上凸殼。
時間復雜度O(nlog^2n)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 100010
#define inf 1e15
int top,n,m;
int p[maxn];
long long ans;
long long sum[maxn];
struct point{
int x;
long long y;
}val[maxn],stack[maxn*];
point operator -(point a,point b){return(point){a.x-b.x,a.y-b.y};}
long long operator *(point a,point b){return a.x*b.y-a.y*b.x;}
struct segment_tree{
int begin,end,size;
}tree[maxn<<];
void push(point x,int tmp){
while (top>tmp && (stack[top]-stack[top-])*(x-stack[top])<=) top--;
stack[++top]=x;
}
void merge(int p){
int lson=p<<,rson=p<<|,lbeg=tree[lson].begin,rbeg=tree[rson].begin,tmp=top+;
tree[p].begin=top+;
while (lbeg<=tree[lson].end && rbeg<=tree[rson].end){
point x;
if (stack[lbeg].x<stack[rbeg].x||(stack[lbeg].x==stack[rbeg].x && stack[lbeg].y<stack[rbeg].y)) x=stack[lbeg++];
else x=stack[rbeg++];
push(x,tmp);
}
while (lbeg<=tree[lson].end) push(stack[lbeg++],tmp);
while (rbeg<=tree[rson].end) push(stack[rbeg++],tmp);
tree[p].end=top;
}
void build(int p,int l,int r){
if (l==r){
tree[p].begin=tree[p].end=++top;
stack[top]=val[l];
return;
}
int mid=(l+r)>>;
build(p<<,l,mid);
build(p<<|,mid+,r);
merge(p);
}
long long calc(int p,int x,int y){return (long long)stack[p].x*(x-y)+stack[p].y+sum[y];}
void solve(int p,int x,int y){
int l=tree[p].begin,r=tree[p].end;
while (r-l>=){
int mid=l+(r-l)/,midmid=r-(r-l)/;
if (calc(mid,x,y)<calc(midmid,x,y)) r=midmid;
else l=mid;
}
for (int i=l;i<=r;i++) ans=min(ans,calc(i,x,y));
}
void query(int p,int l,int r,int x,int y,int tmpx,int tmpy){
if (x<=l && r<=y){
solve(p,tmpx,tmpy);
return;
}
int mid=(l+r)>>;
if (x<=mid) query(p<<,l,mid,x,y,tmpx,tmpy);
if (y>mid) query(p<<|,mid+,r,x,y,tmpx,tmpy);
}
int main(){
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++) scanf("%d",&p[i]),sum[i]=sum[i-]+p[i],val[i]=(point){p[i],(long long)i*p[i]-sum[i]};
build(,,n);
for (int i=,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
x^=ans,y^=ans;
ans=inf;
query(,,n,y-x+,y,x,y);
printf("%lld\n",ans);
}
return ;
}
T3
總結
以上是生活随笔為你收集整理的2016.08.06计算几何总结测试day1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 部队里副班长是干什么的
- 下一篇: WPF 类型“System.Compon