计算几何小结
计算几何小结!
零,说在前面
(转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8379553.html )
这次总结的是计算几何!
根据学长们的观点,计算几何是一类0or100的题目
但是事实上,计算几何题目很考验码力和细节处理能力,以及数学上平几和立体几何那堆东西……
我个人觉得这样的题目是对个人很好的锻炼,虽然我考场上还是只能打暴力罢了2333
那么我们开始总结吧!
一,凸包
这大概是我们的万恶之源了……这里我把之前的凸包总结搬过来
然后续写两句现在的理解
凸包其实是一种最优化的体现,它通过把处理的对象从全集减少到一些“更可能成为最优答案”的点上,从而使我们能更快得到答案
这点尤为体现在DP的斜率优化上
当然,凸包也在数学和几何方面毒瘤着发挥着作用,也有很多巧妙的题目,比如上面那道uoj243破坏导蛋的处理,那个是我的确没有想到的
当然也有关于凸包的很多算法,但是维护凸包的算法只是工具,重要的还是凸包体现的最优化思想
我觉得在我做过的凸包的题里面给我印象最深的就是uoj319分身术了
充分的利用了题目k比较小的条件,维护了子区间的凸包并且合并,从而支持了快速查询
这是我自从联赛前打模拟题(杀蚂蚁等等)以来打过的最考码力的一道题,打完之后的确感觉码力得到提升2333虽然现在还是咸鱼的打不出来
然而如果考出来还是只能打暴力……
二,旋转卡壳
施工ing……
三,半平面交
这玩意其实和高考数学的线性规划是一个东西……
以前的总结正在补……
先写下最近做的题目……
UOJ#242 破坏蛋糕
施工ing……
UOJ#243 破坏导蛋
题意很简洁,但是很巧妙也很难想
我自己能想出加起来总共70pts……
前20pts暴力不用说啦……
中间有20pts坐标范围很小,我们可以维护每个x坐标y的前缀和,然后对于每次询问暴力查询
这样的复杂度是$O(n+200^{2}+200m)$的,可以通过那部分数据
然后后面有一个很多直线平行的部分分,我觉得可以对于每种直线,维护每个点ax+by的值,然后查询……
这个东西应该可以搞个分块维护下,块内存个排序,整块用二分,散点暴力
这样加起来是70pts
后面的我还不会做,正在想……
我们还是怂题解吧,这个太巧妙了
我们上面那个维护查询的问题是每一种ax+by都要处理一次,但是我们可以发现对于某两个点,他们只会在特定的(a,b)处相等
这样,不同的序列只有$O(n^{2})$级别
然后我们考虑分块跑这个$n^2$,然后维护一个bitset表示合法
……操作很辣眼睛,我只能将近看懂jiry的标程
啊……我把带注释的std存在这……
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <algorithm> 5 #include <cstring> 6 #include <bitset> 7 using namespace std; 8 #define LL long long 9 10 const int size=200; 11 struct point 12 { 13 int x,y; 14 double getw(int flag=0) 15 { 16 if (flag) return atan2(-y,x); 17 return atan2(y,x); 18 } 19 void scan(){scanf("%d%d",&x,&y);} 20 21 }A[51000],B[size+10],f[size+10]; 22 23 point operator - (point k1,point k2) {return (point){k1.x-k2.x,k1.y-k2.y};} 24 int n,N,m,ans[110000],a[size+10],where[size+10],wa,wb; 25 double w[size+10]; 26 bitset<size+10>an[110010],C[size+10],tot; 27 struct ask 28 { 29 int a,b,where; 30 LL c; 31 double w; 32 void set(point k1,point k2) 33 {a=k2.y-k1.y,b=-(k2.x-k1.x),c=-((LL)k1.x*a+(LL)k1.y*b),w=atan2(a,b);} 34 35 int pd(point k1){return (LL)k1.x*a+(LL)k1.y*b+c<=0;} 36 37 void rev(){a=-a,b=-b,c=-c,w=atan2(a,b);} 38 }x[310000]; 39 40 int compare(ask k1,ask k2){return k1.w<k2.w;} 41 42 int compare2(int k1,int k2){return w[k1]<w[k2];} 43 44 const double pi=acos(-1); 45 46 int compare3(int k1,int k2)//comp ax+by 47 {return (LL)B[k1].x*wa+(LL)B[k1].y*wb<(LL)B[k2].x*wa+(LL)B[k2].y*wb;} 48 49 struct atom{int u,v;double w;}y[110000]; 50 51 int len; 52 53 LL k1xk2(point k1,point k2){return (LL)k1.x*k2.y-(LL)k1.y*k2.x;} 54 55 int compare4(const atom &k1,const atom &k2){return k1.w<k2.w;} 56 57 int check(atom k1,atom k2)//两向量平行 58 { 59 point a=B[k1.v]-B[k1.u],b=B[k2.v]-B[k2.u]; 60 return k1xk2(a,b)==0&&(LL)a.x*b.x>=0&&(LL)a.y*b.y>=0; 61 } 62 63 int check(point k1,point k2)//相当于上面的comp3,比较了ax+by 64 {return (LL)k1.x*wa+(LL)k1.y*wb<(LL)k2.x*wa+(LL)k2.y*wb;} 65 66 void gopre(int k)//由于a和b的更改带来的顺序更改 67 { 68 while ( where[k]>1 &&check( B[k],B[ a[where[k]-1] ] )) 69 { 70 int k1=where[k],k2=a[k1-1]; 71 swap(a[k1],a[k1-1]); 72 swap(f[k1],f[k1-1]); 73 C[k1-1][k]=1; C[k1-1][k2]=0; 74 where[k]--; where[k2]++; 75 } 76 } 77 void getw(int k1) 78 { 79 int l=1,r=n+1,ans=n+1; 80 LL aa=x[k1].a,b=x[k1].b,c=x[k1].c; 81 while (l<r) 82 { 83 int mid=(l+r)>>1; 84 if(aa*f[mid].x+b*f[mid].y+c<=0)l=mid+1; 85 else ans=mid,r=mid; 86 } 87 an[x[k1].id]&=C[ans-1]; 88 } 89 90 91 int sign,pd[size+10],s[size+10],head; 92 void insert(int k1) 93 { 94 if (pd[k1]!=sign) 95 pd[k1]=sign,s[++head]=k1; 96 } 97 98 99 void solve(){ 100 memset(an,0x00,sizeof an); 101 102 tot=0; 103 for(int i=1;i<=n;i++)tot[i]=1; 104 for (int i=1;i<=m/3;i++)an[i]=tot; 105 wa=0; wb=-1; 106 107 for (int i=1;i<=n;i++)a[i]=i; 108 sort(a+1,a+n+1,compare3);//comp ax+by 109 for (int i=1;i<=n;i++)//记录每个点现在的排名,然后f是排序后的结果 110 where[a[i]]=i,f[i]=B[a[i]]; 111 for (int i=1;i<=n;i++)//C是点集前缀和 112 C[i]=C[i-1],C[i][a[i]]=1; 113 114 len=0; 115 for(int i=1;i<=n;i++) 116 for(int j=1;j<=n;j++) 117 if(i^j)//由j指向i的向量,包括id以及……极角?这个极角是什么操作? 118 //这大概就是题解中所说的关键点吧…… 119 //如果按照我昨天推的转化式子这玩意是对的,a等于-y,b等于x 120 { 121 y[++len]=(atom){ j,i,(B[i]-B[j]).getw(1) }; 122 if(B[i].y==B[j].y) 123 { 124 if(B[j].x>B[i].x)y[len].w=-pi; 125 else y[len].w=0; 126 } 127 } 128 sort(y+1,y+len+1,compare4);//按极角排序? 129 int pre=1,now=1; y[len+1].w=pi; 130 for (int i=1;i<=len+1;i++) 131 { 132 while(now<=m&&x[now].w<=y[i].w)getw(now++); 133 //还没达到下一个关键点,现在可以更新答案 134 if ( ( i!=len+1 && check(y[i],y[i+1])==0 ) || i==len ) 135 {//到达关键点(与下一个不平行) 136 wa=-(B[y[i].v].y-B[y[i].u].y); 137 wb=B[y[i].v].x-B[y[i].u].x; 138 // 139 point pre1=(point){wa,wb}; 140 if(i==len)wb--;//进行一些偏移 141 142 else 143 wa-=B[y[i+1].v].y-B[y[i+1].u].y, 144 wb+=B[y[i+1].v].x-B[y[i+1].u].x; 145 if(k1xk2(pre1,(point){wa,wb})==0)//共线?刚才不是判了么? 146 wa=pre1.y,wb=-pre1.x; 147 148 head=0;sign++; 149 for (int j=pre;j<=i;j++) 150 insert(where[y[j].u]),insert(where[y[j].v]); 151 //插入这段时间内新的点 152 sort(s+1,s+head+1); 153 for (int j=1;j<=head;j++)//进行交换 154 gopre(a[s[j]]); 155 pre=i+1; 156 } 157 } 158 for (int i=1;i<=m/3;i++) ans[i]+=an[i].count(); 159 } 160 int main(){ 161 162 163 164 scanf("%d%d",&N,&m); 165 for (int i=1;i<=N;i++) A[i].scan(); 166 for (int i=1;i<=m;i++){ 167 point k1,k2,k3; k1.scan(); k2.scan(); k3.scan(); 168 x[i*3-2].set(k1,k2); 169 x[i*3-1].set(k2,k3); 170 x[i*3].set(k3,k1); 171 if (x[i*3-2].pd(k3)==0) x[i*3-2].rev(); 172 if (x[i*3-1].pd(k1)==0) x[i*3-1].rev(); 173 if (x[i*3].pd(k2)==0) x[i*3].rev(); 174 } 175 176 177 178 m*=3; 179 for (int i=1;i<=m;i++) x[i].id=(i-1)/3+1; 180 sort(x+1,x+m+1,compare); 181 for (int l=1;l<=N;l+=size) 182 { 183 int r=min(N,l+size-1); 184 n=r-l+1; 185 for(int i=l;i<=r;i++) 186 B[i-l+1]=A[i]; 187 solve(); 188 } 189 m/=3; 190 for (int i=1;i<=m;i++) printf("%d\n",ans[i]); 191 return 0; 192 }
四,Simpson积分
施工ing……
五,没做两道题的其他专题
一,平面图转对偶图
施工ing……
二,随机化算法
1.最小圆覆盖
施工ing……
三,三维几何
施工ing……
六,总结
施工ing……