codeforces计算几何刷题记录【每日一题】
codeforces里面的计算几何我平时都有刷,但是总是懒懒散散的,直到昨天的一场训练赛,作为计算几何选手的我被一道计算几何题卡了几乎整场,虽然这题是防ak题,但赛后补出来发现并不是很难,主要原因还是我计算几何太弱了。于是乎,蒟蒻我下定决心从今天开始,每天至少刷一道计算几何(其他时间可能要补补cf或者学学其他算法的,所以就不强制整天练计几了)。
以此记录!!codeforces 的problemset,tag选择计算几何,按照难度排序,从第一篇最后一题往上刷。加油!!!。
(后补:发现cf上面有些计几题其实考察的不是计几,所以就挑一些比较计几的计几题来做了,然后也不局限于cf了,反正每天坚持一道不水的计几就好了)
https://codeforces.com/problemset/page/1?tags=geometry&order=BY_RATING_DESC
开始!!
2020.4.6: 第一题:Delivering Carcinogen
二分然后套个圆的板子就ok了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-6 4 const double pi = acos(-1.0); 5 struct Point{ 6 double x,y,v; 7 Point rotate(double deta){ 8 return (Point){ x * cos(deta) - y*sin(deta),x*sin(deta) + y * cos(deta)}; 9 } 10 }a,b; 11 double dist(Point a,Point b){ 12 return sqrt( (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y-b.y)); 13 } 14 double cross(Point a,Point b,Point c){ 15 return (b.x - a.x) * (c.y-a.y) - (c.x - a.x) * (b.y - a.y); 16 } 17 struct Circle{ 18 Point o; 19 double r; 20 //圆心出发的射线的交点 21 Point getPoint(double deta){ 22 return (Point){o.x + cos(deta)*r , o.y + sin(deta)*r}; 23 } 24 //两个圆上的点的最短圆弧 25 double getPointDis(Point a,Point b){ 26 double deta = fabs( atan2(a.y - o.y,a.x - o.x) - atan2(b.y - o.y,b.x - o.x)); 27 deta = min(deta,2*pi - deta); 28 return deta * r; 29 } 30 //ab线段和圆是否相交 31 bool is_insert(Point a,Point b){ 32 double ab = dist(a,b); 33 double d = fabs(cross(o,a,b)) / ab; 34 if( d > r - eps) return 0; 35 double oa = dist(a,o) , ob = dist(o,b); 36 double ta = (oa*oa + ab*ab - ob*ob)/2*oa*ab; 37 double tb = (ob*ob + ab*ab - oa*oa)/2*oa*ab; 38 if(ta < -eps || tb < -eps) return 0; 39 return 1; 40 } 41 }cir; 42 //求切点 43 void getTangentPoints(Point p,Circle C , Point& r1,Point& r2){ 44 double d = dist(p,C.o); 45 double base = atan2(p.y - C.o.y,p.x - C.o.x); 46 double deta = acos( C.r/d ); 47 r1 = C.getPoint(base - deta); 48 r2 = C.getPoint(base + deta); 49 } 50 int sgn(double x){ 51 if( fabs(x) < eps) return 0; 52 if( x > 0) return 1; 53 return -1; 54 } 55 bool judge(double t){ 56 double R = sqrt( a.x * a.x + a.y * a.y); 57 double w = a.v / R ; 58 Point c = a.rotate(w * t); 59 if(cir.is_insert(b,c) == 0 ) return dist(b,c) < t*b.v + eps; 60 Point cutb1 , cutb2; 61 getTangentPoints(b,cir,cutb1,cutb2); 62 Point cutc1 , cutc2; 63 getTangentPoints(c,cir,cutc1,cutc2); 64 double tem1 = dist(b,cutb2) + dist(c,cutc1) + cir.getPointDis(cutb2,cutc1); 65 double tem2 = dist(b,cutb1) + dist(c,cutc2) + cir.getPointDis(cutb1,cutc2); 66 return min( tem1,tem2 ) < t*b.v + eps; 67 } 68 int main(){ 69 scanf("%lf %lf %lf",&a.x,&a.y,&a.v); 70 scanf("%lf %lf %lf %lf",&b.x,&b.y,&b.v,&cir.r); 71 cir.o = (Point){0,0}; 72 double le = 0 , ri = 100000000; 73 double res; 74 while( ri - le > eps){ 75 double m = (ri+le)/2; 76 if(judge(m)){ 77 res = m; 78 ri = m; 79 } 80 else le = m; 81 } 82 printf("%.9f",res); 83 return 0; 84 }
2020.4.7:第二题:Kingdom Trip (K题)
这题我们把两两是否有边建立出来,然后直接来个n方dp就好了。问题是怎么建边。
我们判断线段ab和圆是否有交,其实是判断圆心到线段距离<R,并且两个非圆心夹角是锐角。然后其实我们还可以过a做圆o的切线,判断b是否在o切线夹角里面,那么假如ab在圆同一侧呢?没关系,我们反过来再做一次判断就好了。具体判断就是,枚举起点i,然后依次往后判断j,然后判断完j之后,把 j 的切线夹角和原来的求个交就好了。
然后其实判断夹角交有一个很好的做法就是:我们把第一个角度ph0设为基准,然后夹角范围我们用相对于ph0的deta来表示,左夹角为ph0 + l , 右夹角为 ph0 + r,那么我们每一次要交的是夹角角度是
[ phj - deta , phj + deta] ,那么我们只需要判断phj 和 ph0 之间的绝对夹角 theta, 然后 l = max(l,theta-deta); r = min(r,theta+deta); 即可。
这样比那种直接把左右角度表示出来好很多,因为左右角度表示出来很容易出现 l >r ,烦得要死。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-7 4 const int N = 2e3+9; 5 #define inf 1e8 6 const double pi = acos(-1.0); 7 struct Point{ 8 double x,y; 9 }p[N]; 10 int dp[N],G[N][N]; 11 double cross(Point a,Point b,Point c){ 12 return (b.x-a.x)*(c.y-a.x) - (c.x-a.x)*(b.y-a.y); 13 } 14 double dist(Point a,Point b){ 15 return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)); 16 } 17 int n; 18 double d; 19 double solve(double x){ 20 if(x>pi+eps) return x - 2*pi; 21 if(x<-pi - eps) return x + 2*pi; 22 return x; 23 } 24 void init(bool fg){ 25 for(int i = 1;i<n;++i){ 26 double ph0 = inf,l,r; 27 for(int j = i+1;j<=n;++j){ 28 double dd = dist(p[i],p[j]); 29 bool ok = 1; 30 double phj = atan2(p[j].y-p[i].y,p[j].x-p[i].x); 31 double theta = solve(phj-ph0); 32 if(ph0 != inf){ 33 if(theta < l - eps || theta > r + eps) ok = 0; 34 } 35 if(ok){ 36 if(fg) G[i][j] = 1; 37 else G[n+1-i][n+1-j] = 1; 38 } 39 if(dd < d - eps) continue; 40 double deta = asin(d/dd); 41 if(ph0 == inf){ 42 ph0 = phj; 43 l = -deta; 44 r = deta; 45 } 46 else{ 47 l = max(l,theta-deta); 48 r = min(r,theta+deta); 49 } 50 } 51 } 52 } 53 void work(){ 54 memset(dp,127,sizeof(dp)); 55 dp[n] = 1; 56 for(int i = n-1;i>=1;--i){ 57 for(int j = i+1;j<=n;++j){ 58 if(G[i][j] && G[j][i]) dp[i] = min(dp[i],dp[j] + 1); 59 } 60 } 61 printf("%d",dp[1]); 62 } 63 int main(){ 64 freopen("kingdom.in", "r", stdin); 65 freopen("kingdom.out", "w", stdout); 66 scanf("%d %lf",&n,&d); 67 for(int i = 1;i<=n;++i) scanf("%lf %lf",&p[i].x,&p[i].y); 68 init(0); 69 reverse(p+1,p+1+n); 70 init(1); 71 // for(int i = 1;i<=n;++i){ 72 // for(int j = 1;j<=n;++j){ 73 // cerr<<i<<" "<<j<<" "<<G[i][j]<<endl; 74 // } 75 // } 76 work(); 77 return 0; 78 }
2020.4.8 第三题 : Split Game
第一象限给你一个简单多边形,问你从原点连一条射线,能分成多边形最多多少份
很明显就是扫描线啦,极角排序然后扫描线,按照每一个点与左右两边的点的位置关系来判断是+1还是-1,需要注意的是,我们平时的扫描线是把+1的放前,-1放后,贪心。但是这里我们不能贪心,一定得把这条线答案统计完再更新答案。还有就是,有一些点是扫描线到这点就更新,有一些点是到这个点之后才更新,所以给他一个fr标记,标记它的前后。
也不是很难。。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+9; 4 typedef long long ll; 5 struct Point{ 6 ll x,y; 7 int id,ty,fr; 8 ll len(){return x*x+y*y;} 9 }p[N],tem[N],p0; 10 ll cross(Point a,Point b,Point c){ 11 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 12 } 13 bool cmp(Point a,Point b){ 14 if(cross(p0,a,b) == 0 ) return a.fr > b.fr; 15 return cross(p0,a,b) > 0; 16 } 17 int n; 18 void init(){ 19 int nn = 0; 20 p[nn++] = tem[0]; 21 for(int i = 1;i<n;++i){ 22 while( nn >= 2 && cross(p[nn-2],p[nn-1],tem[i]) == 0 ){ 23 --nn; 24 } 25 p[nn++] = tem[i]; 26 } 27 n = nn; 28 } 29 int main(){ 30 scanf("%d",&n); 31 p0.x = p0.y = 0; 32 for(int i = 0;i<n;++i){ 33 scanf("%lld %lld",&tem[i].x,&tem[i].y); 34 } 35 init(); 36 for(int i = 0;i<n;++i) p[i].id = i; 37 for(int i = 0;i<n;++i){ 38 Point l = p[ (i - 1 + n ) % n]; 39 Point r = p[ (i + 1 ) % n]; 40 ll cl = cross(p0,p[i],l) , cr = cross(p0,p[i],r); 41 if( cl > 0 && cr > 0 ){ 42 p[i].ty = 1; 43 if(cross(p[i],l,r) > 0 ) p[i].fr = 1; 44 else p[i].fr = -1; 45 } 46 else if( cl < 0 && cr < 0 ){ 47 p[i].ty = -1; 48 if(cross(p[i],l,r) > 0 ) p[i].fr = -1; 49 else p[i].fr = 1; 50 } 51 else if(cl == 0){ 52 Point llp = p[ (i - 2 + n ) % n]; 53 ll cll = cross(p0,p[i],llp); 54 if(cll > 0 && cr > 0) p[i].ty = 1; 55 else if( cll < 0 && cr < 0 ) p[i].ty = -1; 56 if(l.len() < p[i].len()) p[i].fr = -1; 57 else p[i].fr = 1; 58 } 59 } 60 ll ans = 1, now = 1; 61 sort(p,p+n,cmp); 62 for(int i = 0;i<n;++i){ 63 // cerr<<p[i].x<<" "<<p[i].y<<" "<<p[i].ty<<" "<<p[i].fr<<endl; 64 if( i > 0 && cross(p0,p[i],p[i-1]) == 0 && p[i].fr == p[i-1].fr ) now += p[i].ty; 65 else{ 66 ans = max(now,ans); 67 now += p[i].ty; 68 } 69 } 70 ans = max(ans,now); 71 printf("%d",ans); 72 return 0; 73 }
2020.4.10 第四题:Alice and Bomb
题意:二维平面有一个点是光源,有n个简单多边形,问从原点走到阴影处最短距离
昨天咕咕咕了一天,今天补回来。这题其实就是一个寻路问题,寻路问题就是扣关键点,然后对关键点连边,最后跑最短路。这题最终答案肯定是走某一条垂线,所以直接跑最短路,连边就是各个顶点以及垂足。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-6 4 const double inf = 1e30; 5 const double pi = acos(-1.0); 6 struct Point{ 7 double x,y; 8 double dis; 9 bool ok; 10 int id; 11 Point(){} 12 Point(double xx,double yy){ 13 x = xx; y = yy; 14 } 15 bool operator < (const Point& b)const{ 16 return dis > b.dis; 17 } 18 Point operator + (const Point& b)const{ 19 return (Point){x+b.x,y+b.y}; 20 } 21 Point operator * (const double& b)const{ 22 return (Point){b*x,b*y}; 23 } 24 }p0,A; 25 struct Line{ 26 Point s,e; 27 double a,b,c; 28 Line(Point p1,Point p2){ 29 s = p1, e = p2; 30 a = p2.y - p1.y; 31 b = p1.x - p2.x; 32 c = p2.x * p1.y - p1.x * p2.y; 33 } 34 }; 35 int n; 36 const int N = 1000+9; 37 vector<Point> pol[N]; 38 double dis[N][N]; 39 double ans[N]; 40 bool can[N]; 41 int cnt; 42 int sgn(double x){ 43 if(fabs(x) < eps ) return 0; 44 if(x>0) return 1; 45 return -1; 46 } 47 double dist(Point a,Point b){ 48 return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)); 49 } 50 double cross(Point a,Point b,Point c){ 51 return (b.x - a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 52 } 53 double dot(Point a,Point b,Point c){ 54 return (b.x - a.x) * (c.x - a.x) + (b.y - a.y) * (c.y - a.y); 55 } 56 Point footOfLine(Line l,Point p){ 57 double t = (l.a * p.x + l.b*p.y + l.c) / (l.a * l.a + l.b * l.b); 58 return p + Point(l.a,l.b) * t; 59 } 60 bool insert(Line l1,Line l2){ 61 return 62 max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) && 63 max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) && 64 max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) && 65 max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) && 66 sgn( cross(l1.s,l2.s,l1.e) ) * sgn( cross(l1.s,l2.e,l1.e)) < 0 && 67 sgn( cross(l2.s,l1.s,l2.e) ) * sgn( cross(l2.s,l1.e,l2.e)) < 0; 68 } 69 bool access(Point a,Point b){ 70 Line l1 = Line(a,b); 71 for(int i = 1;i<=n;++i){ 72 for(int k = 0;k<pol[i].size()-1;++k){ 73 Line l2 = Line(pol[i][k],pol[i][k+1]); 74 // cerr<<l2.s.x<<" "<<l2.s.y<<" "<<l2.e.x<<" "<<l2.e.y<<endl; 75 if( insert(l1,l2) ) return 0; 76 // cerr<<"hh"<<endl; 77 } 78 } 79 return 1; 80 } 81 void solve(){ 82 for(int i = 1;i<=cnt;++i){ 83 for(int j = i+1;j<=cnt;++j){ 84 dis[i][j] = dis[j][i] = inf; 85 } 86 } 87 for(int i = 1;i<=n;++i){ 88 for(int j = i+1;j<=n;++j){ 89 for(int a = 0;a<pol[i].size()-1;++a){ 90 for(int b = 0;b<pol[j].size()-1;++b){ 91 if(access(pol[i][a],pol[j][b])){ 92 dis[pol[i][a].id][pol[j][b].id] = dis[pol[j][b].id][pol[i][a].id] = dist(pol[i][a],pol[j][b]); 93 } 94 dis[pol[i][a].id][pol[j][b].id] = dis[pol[j][b].id][pol[i][a].id] = inf; 95 } 96 } 97 } 98 } 99 for(int i = 1;i<=n;++i){ 100 for(int j = 0;j<pol[i].size()-1;++j){ 101 dis[pol[i][j].id][pol[i][j+1].id] = dis[pol[i][j+1].id][pol[i][j].id] = dist(pol[i][j],pol[i][j+1]); 102 } 103 } 104 for(int i = 1;i<=n;++i){ 105 for(int j = 0;j<pol[i].size() - 1;++j){ 106 if(cross(p0,pol[i][j],pol[i][j+1]) > eps){ 107 can[pol[i][j].id] = can[pol[i][j+1].id] = 1; 108 // cerr<<"can"<<endl; 109 } 110 } 111 } 112 priority_queue<Point> q; 113 A.dis = 0; 114 A.ok = A.id = 0; 115 q.push(A); 116 while(!q.empty()){ 117 Point now = q.top(); 118 q.pop(); 119 // cerr<<now.x<<" "<<now.y<<" "<<now.dis<<" "<<now.ok<<" now"<<endl; 120 if(now.ok){ 121 // cerr<<now.x<<" "<<now.y<<endl; 122 printf("%.8f\n",now.dis); 123 break; 124 } 125 for(int i = 1;i<=n;++i){ 126 for(int k = 0;k<pol[i].size() - 1;++k){ 127 int idb = pol[i][k].id; 128 if(now.id){ 129 if(dis[now.id][idb] != inf && now.dis + dis[now.id][idb] < ans[idb]){ 130 ans[idb] = now.dis + dis[now.id][idb]; 131 Point tem = pol[i][k]; 132 tem.dis = ans[idb]; 133 tem.ok = 0; 134 // cerr<<tem.dis<<" "<<tem.x<<" "<<tem.y<<" ans"<<endl; 135 q.push(tem); 136 } 137 } 138 else{ 139 if(access(now,pol[i][k]) && now.dis + dist(pol[i][k],now) < ans[idb]){ 140 ans[idb] = now.dis + dist(now,pol[i][k]); 141 // cerr<<idb<<" "<<dist(pol[i][k],now)<<endl; 142 Point tem = pol[i][k]; 143 tem.dis = ans[idb]; 144 tem.ok = 0; 145 // cerr<<tem.x<<" "<<tem.y<<" "<<tem.dis<<" idb"<<endl; 146 q.push(tem); 147 } 148 } 149 } 150 } 151 for(int i = 1;i<=n;++i){ 152 for(int k = 0;k<pol[i].size()-1;++k){ 153 if(can[pol[i][k].id] && ( dot(pol[i][k],p0,now) < eps) ){ 154 Point tem = footOfLine(Line(p0,pol[i][k]) , now ); 155 tem.dis = now.dis + dist(now,tem); 156 // cerr<<"okk"<<endl; 157 // cerr<<tem.dis<<endl; 158 tem.id = 0 , tem.ok = 1; 159 // cerr<<tem.x<<" "<<tem.y<<" "<<tem.dis<<" tem"<<endl; 160 q.push(tem); 161 } 162 } 163 } 164 } 165 // cerr<<"fuck"<<endl; 166 } 167 int main(){ 168 // cerr<<sqrt(5)<<endl; 169 A.x = 0, A.y = 0; 170 while(~scanf("%d",&n) && n){ 171 cnt = 0; 172 scanf("%lf %lf",&p0.x,&p0.y); 173 for(int i = 1;i<=n;++i){ 174 pol[i].clear(); 175 int m; scanf("%d",&m); 176 Point tem; 177 for(int j = 1;j<=m;++j){ 178 scanf("%lf %lf",&tem.x,&tem.y); 179 tem.id = ++cnt; 180 ans[cnt] = inf; 181 can[cnt] = 0; 182 pol[i].push_back(tem); 183 } 184 pol[i].push_back(pol[i][0]); 185 } 186 if(access(p0,A) == 0){ 187 printf("0.00000000\n"); 188 continue; 189 } 190 // cerr<<"here"<<endl; 191 solve(); 192 } 193 }
2020.4.11 第五题:Symmetry
题意:给你一个简单多边形,顺序乱序,问你这个是否轴对称图形。
我们枚举第一个点的对称点,然后再On判断即可,记得还要判断轴上的点是否大于2个。
假如第一个点在轴上?没关系,因为轴上的点不可能大于2个,不然多边形自交,所以我们同样工作做三次即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-6 4 const double inf = 1e30; 5 const int N = 1000+8; 6 const double pi = acos(-1.0); 7 struct Point{ 8 double x,y; 9 Point operator - (const Point& b)const{ 10 return (Point){x-b.x,y-b.y}; 11 } 12 Point operator + (const Point& b)const{ 13 return (Point){x+b.x,y+b.y}; 14 } 15 Point operator * (const double& b)const{ 16 return (Point){b*x,b*y}; 17 } 18 bool operator < (const Point& b)const{ 19 if(x == b.x) return y < b.y; 20 return x < b.x; 21 } 22 double mul(const Point& b)const{ 23 return x*b.x + b.y*y; 24 } 25 void near(){ 26 int cx = ceil(x) , fx = floor(x); 27 int cy = ceil(y) , fy = floor(y); 28 if( fabs(cx - x) < eps ) x = cx; 29 else if(fabs(fx - x) < eps) x = fx; 30 else x = inf; 31 32 if(fabs(cy - y) < eps) y = cy; 33 else if(fabs(fy - y) < eps ) y = fy; 34 else y = inf; 35 } 36 }p[N]; 37 bool show; 38 struct Line{ 39 Point s,e; 40 }; 41 map<Point,int> has; 42 int n; 43 int sgn(double x){ 44 if(fabs(x) < eps ) return 0; 45 if( x>0 ) return 1; 46 return -1; 47 } 48 double cross(Point a,Point b,Point c){ 49 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 50 } 51 double dist2(Point a,Point b){ 52 return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); 53 } 54 double dot(Point a,Point b,Point c){ 55 return (b.x-a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y); 56 } 57 Point projection(Line l,Point p){ 58 double t = dot(l.s,p,l.e) / dist2(l.s,l.e); 59 return l.s + (l.e - l.s) * t; 60 } 61 Point reflection(Line l,Point p){ 62 return p + (projection(l,p) - p) * 2; 63 } 64 bool onLine(Point a,Point b,Point c){ 65 return sgn(cross(a,b,c)) == 0; 66 } 67 bool judge(Point s,Point e,Point a,Point b){ 68 Point mid = (a+b)*0.5; 69 return onLine(s,e,mid) && (sgn( (e-s).mul(b-a) ) == 0) ; 70 } 71 bool solve(){ 72 int cnt = 0; 73 for(int cas = 1;cas<=3;++cas){ 74 for(int k = 2;k<=n;++k){ 75 cnt = 0; 76 Point a = (p[1] + p[k]) * 0.5; 77 Point b = p[k] - p[1]; 78 swap(b.x,b.y); 79 b.x = -b.x; 80 b = a + b; 81 Line l = (Line){a,b}; 82 for(int i = 1;i<=n;++i) has[ p[i] ] = 1; 83 has[ p[1] ] = has[ p[k] ] = 0; 84 bool ok = 1; 85 for(int i = 1;i<=n && ok;++i){ 86 if(has[p[i]] == 0) continue; 87 Point tem = reflection(l,p[i]); 88 tem.near(); 89 if(tem.x == inf || tem.y == inf || (has[tem] == 0)) ok = 0; 90 if(judge(l.s,l.e,p[i],tem) == 0) ok = 0; 91 has[tem] = has[p[i]] = 0; 92 if(tem.x == p[i].x && tem.y == p[i].y) ++cnt; 93 } 94 if(ok && cnt<=2) return 1; 95 } 96 97 Point tem = p[1]; 98 for(int i =1;i<n;++i) p[i] = p[i+1]; 99 p[n] = tem; 100 } 101 return 0; 102 } 103 bool all_Line(){ 104 for(int i = 3;i<=n;++i){ 105 if( sgn( cross(p[1],p[2],p[i]) ) != 0 ) return 0; 106 } 107 return 1; 108 } 109 int main(){ 110 scanf("%d",&n); 111 for(int i = 1;i<=n;++i) scanf("%lf %lf",&p[i].x,&p[i].y); 112 if(all_Line()){ 113 puts("No"); 114 return 0; 115 } 116 if(solve()) puts("Yes"); 117 else puts("No"); 118 return 0; 119 }
2020.4.12 第六题:集训队训练:Forest protection
这题虽然到最后很少人过,但是其实很简单,直接当作签到题秒了,就求个凸包然后旋转卡壳一下好了。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 1e5+9; 5 struct Point{ 6 ll x,y; 7 bool operator < (const Point& b)const{ 8 return x < b.x || (x==b.x && y < b.y); 9 } 10 Point operator - (const Point& b)const{ 11 return (Point){x-b.x,y-b.y}; 12 } 13 ll cro(Point b){ 14 return x*b.y - b.x*y; 15 } 16 ll dot(Point b){ 17 return x*b.x + y * b.y; 18 } 19 }p[N],ch[N*2]; 20 int n; 21 ll cross(Point a,Point b,Point c){ 22 return (b.x - a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 23 } 24 int Andrew(){ 25 sort(p,p+n); 26 int m = 0; 27 for(int i = 0;i<n;++i){ 28 while( m>1 && cross(ch[m-2],p[i],ch[m-1]) > 0 ) --m; 29 ch[m++] = p[i]; 30 } 31 int k = m; 32 for(int i = n-2;i>=0;--i){ 33 while( m>k && cross(ch[m-2],p[i],ch[m-1]) >0 ) --m; 34 ch[m++] = p[i]; 35 } 36 if(n>1) --m; 37 return m; 38 } 39 bool all_line(){ 40 for(int i = 2;i<n;++i){ 41 if(cross(p[0],p[1],p[i]) != 0) return 0; 42 } 43 return 1; 44 } 45 int main(){ 46 scanf("%d",&n); 47 for(int i = 0;i<n;++i) scanf("%lld %lld",&p[i].x,&p[i].y); 48 if( all_line() ){ 49 printf("%d",n); 50 return 0; 51 } 52 int m = Andrew(); 53 int ans = 0; 54 for(int i = 0;i<m;++i) ch[i+m] = ch[i]; 55 int j = 0; 56 for(int i = 0;i<m;++i){ 57 while( j<i+m && ( ( (ch[i+1] - ch[i]).cro(ch[j+1] - ch[j]) == 0) && ( ch[i+1] - ch[i]).dot(ch[j+1] - ch[j]) > 0 ) ) ++j; 58 while( j < i+m && (ch[i+1] - ch[i]).cro(ch[j+1] - ch[j]) > 0 ) ++j; 59 // cerr<<ch[i].x<<" "<<ch[i].y<<" "<<ch[j].x<<" "<<ch[j].y<<" "<<i<<" "<<j<<endl; 60 ans = max(j - i -1,ans); 61 } 62 printf("%d",m - ans); 63 return 0; 64 }
2020.4.13 第七题(H): Cornering at Poles
题意:给你8个圆,问你从原点走到终点,不能经过任意圆的最短距离。
这题是一道寻路问题的经典题目。其实不是很难,但是老是写挂细节,搞得心态崩了。。。写了两天。
就是很明显,我两个点的最短距离肯定就是点到圆的切线,圆上的弧,两圆切线。可以想像一下一根直线拉直。
然后就是把模板抄上去,建图跑dij就好了。 注意:弧上两点判断是否可以达到,只需要判断两个点是不是在某一个圆里,或者某一个圆的圆心极角在 两点极角之间,然后 可以那部分圆弧就取min就好了。
可以把关键点全部扣出来,然后n方建边,也可以边扣关键点边建边。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-8 4 #define pii pair<double,int> 5 const double inf = 1e50; 6 const double pi = acos(-1.0); 7 const int N = 1e5+9; 8 double R; 9 int sgn(double x){ 10 if(fabs(x) < eps) return 0; 11 if( x > 0 ) return 1; 12 return -1; 13 } 14 struct Point{ 15 double x,y; 16 Point operator - (const Point& b)const{ 17 return (Point){x-b.x,y-b.y}; 18 } 19 Point operator + (const Point& b)const{ 20 return (Point){ x + b.x,y+b.y}; 21 } 22 Point operator * (const double& b)const{ 23 return (Point){x*b,y*b}; 24 } 25 Point operator / (const double& b)const{ 26 return (Point){x/b,y/b}; 27 } 28 }p[N]; 29 30 double cross(Point a,Point b,Point c){ 31 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 32 } 33 double dot(Point a,Point b,Point c){ 34 return (b.x - a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y); 35 } 36 double dist2(Point a,Point b){ 37 return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); 38 } 39 double dist(Point a,Point b){ 40 return sqrt(dist2(a,b)); 41 } 42 double Point_to_Seg(Point p,Point a,Point b){ 43 if( sgn( dot(a,p,b) ) < 0 ) return dist(p,a); 44 if( sgn( dot(b,p,a) ) < 0 ) return dist(p,b); 45 return fabs( cross(p,a,b) ) / dist(a,b); 46 } 47 struct Circle{ 48 Point o; 49 vector<int> v; 50 Point getPoint(double deta){ 51 return (Point){o.x + cos(deta)*R , o.y + sin(deta)*R }; 52 } 53 bool is_insert(Point a,Point b){ 54 double d; 55 if( dist(a,b) < eps ) d = dist(a,o); 56 else d = Point_to_Seg(o,a,b); 57 if( sgn(d - R) < 0 ) return 1; 58 return 0; 59 } 60 }cir[10]; 61 int cnt; 62 int bel[N]; 63 vector< pair<int,double> > G[N]; 64 double dis[N]; 65 Point a[10],b[10]; 66 int n; 67 int getTangentPoints(Point p,Circle c,Point& r1,Point& r2){ 68 double d = dist(p,c.o); 69 if( sgn(d-R) < 0 ) return 0; 70 else if( sgn(d-R) == 0 ){ 71 r1 = p; 72 return 1; 73 } 74 else{ 75 double base = atan2(p.y - c.o.y,p.x - c.o.x); 76 double deta = acos( R/d ); 77 r1 = c.getPoint(base - deta); 78 r2 = c.getPoint(base + deta); 79 return 2; 80 } 81 } 82 int getCircleTangents(Circle A,Circle B,Point* a,Point* b){ 83 int cnt = 0; 84 double d2 = dist2(A.o,B.o); 85 double rsum = 2*R; 86 double base = atan2(B.o.y - A.o.y,B.o.x - A.o.x); 87 double ang = acos(0.0); 88 a[cnt] = A.getPoint(base+ang); b[cnt] = B.getPoint(base+ang); cnt++; 89 a[cnt] = A.getPoint(base-ang); b[cnt] = B.getPoint(base-ang); cnt++; 90 91 if( sgn(d2 - rsum*rsum) > 0 ){ 92 ang = acos( 2*R/ sqrt(d2) ); 93 a[cnt] = A.getPoint(base + ang); b[cnt] = B.getPoint(pi+base + ang); ++cnt; 94 a[cnt] = A.getPoint(base - ang); b[cnt] = B.getPoint(pi+base - ang); ++cnt; 95 } 96 return cnt; 97 } 98 bool road_ok(Point a,Point b){ 99 for(int i = 1;i<=n;++i){ 100 if( cir[i].is_insert(a,b) ) return 0; 101 } 102 return 1; 103 } 104 // double relative(double a){ 105 // if( a < -pi ) a += 2*pi; 106 // else if( a > pi) a -= 2*pi; 107 // return a; 108 // } 109 double rad_ok(int k,Point a,Point b){ 110 Circle c = cir[k]; 111 double le = atan2(a.y - c.o.y,a.x - c.o.x); 112 double ri = atan2(b.y - c.o.y,b.x - c.o.x); 113 if( le > ri ) swap(le,ri); 114 bool ok1 = 1, ok2 = 1; 115 for(int i = 1;i<=n;++i){ 116 if( dist(a,cir[i].o) < R - eps || dist( b,cir[i].o) < R - eps ) return inf; 117 double d = dist(cir[i].o,cir[k].o); 118 if( i == k || dist(cir[i].o,c.o) > 2*R - eps) continue; 119 Point w = (cir[i].o - cir[k].o) / d * 100.0 + cir[k].o; 120 // double phi = atan2( cir[i].o.y - c.o.y , cir[i].o.x - c.o.x); 121 double phi = atan2(w.y-c.o.y,w.x - c.o.x); 122 if( sgn(phi - le) > 0 && sgn(phi - ri) < 0 ) ok1 = 0; 123 else ok2 = 0; 124 } 125 double res = inf; 126 // cerr<<(ri-le)*R<<" "<<(2*pi - ri + le)*R<<" !!!"<<ok1<<" "<<ok2<<endl; 127 if(ok1) res = min(res,(ri - le)*R ); 128 if(ok2) res = min(res, (2*pi - ri + le) * R); 129 return res; 130 } 131 void build_G(){ 132 for(int i = 1;i<=n;++i){ 133 Point r1,r2; 134 int m = getTangentPoints(p[1],cir[i],r1,r2); 135 if( m == 0 ) return; 136 if(m>=1 ){ 137 p[++cnt] = r1; bel[cnt] = i; 138 } 139 if(m >= 2){ 140 p[++cnt] = r2; bel[cnt] = i; 141 } 142 143 m = getTangentPoints(p[2],cir[i],r1,r2); 144 if( m == 0 ) return; 145 if(m>=1 ){ 146 p[++cnt] = r1; bel[cnt] = i; 147 } 148 if(m >= 2){ 149 p[++cnt] = r2; bel[cnt] = i; 150 } 151 } 152 153 for(int i = 1;i<=n;++i){ 154 for(int j = i+1;j<=n;++j){ 155 int m = getCircleTangents(cir[i],cir[j],a,b); 156 for(int k = 0;k<m;++k){ 157 p[++cnt] = a[k]; bel[cnt] = i; 158 p[++cnt] = b[k]; bel[cnt] = j; 159 } 160 } 161 } 162 for(int i = 1;i<=cnt;++i){ 163 for(int j = i+1;j<=cnt;++j){ 164 if( bel[i] == bel[j] ){ 165 double d = rad_ok(bel[i],p[i],p[j]); 166 // cerr<<i<<" "<<j<<" "<<d<<endl; 167 if( d != inf ){ 168 G[i].push_back({j,d}); 169 G[j].push_back({i,d}); 170 } 171 } 172 else{ 173 if(road_ok(p[i],p[j])){ 174 G[i].push_back({j,dist(p[i],p[j])}); 175 G[j].push_back({i,dist(p[i],p[j])}); 176 } 177 } 178 } 179 } 180 } 181 void dij(){ 182 for(int i = 1;i<=cnt;++i) dis[i] = inf; 183 dis[1] = 0; 184 priority_queue<pii,vector<pii>,greater<pii> > q; 185 q.push(make_pair(0,1)); 186 while(!q.empty()){ 187 int u = q.top().second; 188 double tem = q.top().first; 189 q.pop(); 190 if( tem > dis[u] + eps ) continue; 191 for(auto it : G[u]){ 192 int v = it.first; 193 double w = it.second; 194 if( dis[v] - eps > dis[u] + w){ 195 dis[v] = dis[u] + w; 196 q.push( make_pair(dis[v],v) ); 197 } 198 } 199 } 200 } 201 int main(){ 202 R = 100.0; 203 scanf("%d",&n); 204 scanf("%lf %lf",&p[2].x,&p[2].y); 205 p[1].x = p[1].y = 0; 206 bel[1] = 0 , bel[2] = n+1; 207 cnt = 2; 208 for(int i = 1;i<=n;++i){ 209 scanf("%lf %lf",&cir[i].o.x,&cir[i].o.y); 210 } 211 build_G(); 212 dij(); 213 214 if( dis[2] == inf ) printf("0.0"); 215 else printf("%.5f",dis[2]); 216 return 0; 217 } 218 /* 219 1 958 834 220 86 57 221 */
2020.4.15.第八题:hdu4785
这题真的难顶,前前后后挂了很多次,可能也是因为晚上思维下降的原因吧。为这题专门写了一个博客:https://www.cnblogs.com/xiaobuxie/p/12712701.html
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-8 4 const int N = 1e3+9; 5 const double inf = 1e6; 6 int sgn(double x){ 7 if( fabs(x) < eps) return 0; 8 if( x < 0 ) return -1; 9 return 1; 10 } 11 struct Point{ 12 double x,y; 13 Point operator - (const Point& b)const{ 14 return (Point){x-b.x,y-b.y}; 15 } 16 Point operator + (const Point& b)const{ 17 return (Point){x+b.x,y+b.y}; 18 } 19 bool operator < (const Point& b)const{ 20 if( sgn(x - b.x) == 0 ) return y < b.y; 21 return x < b.x; 22 } 23 }va[N],vb[N],rec[10]; 24 double cross(Point a,Point b){ 25 return a.x * b.y - b.x * a.y; 26 } 27 vector<Point> a[N],b[N]; 28 int n; 29 double xl,yl,xr,yr; 30 double X[N]; 31 double mi[N],mx[N]; 32 int cnt; 33 bool insert(Point a,Point b,Point c,Point d){ 34 return 35 max(a.x,b.x) >= min(c.x,d.x) && 36 max(c.x,d.x) >= min(a.x,b.x) && 37 max(a.y,b.y) >= min(c.y,d.y) && 38 max(c.y,d.y) >= min(a.y,b.y) && 39 sgn(cross(c-a,b-a))*sgn(cross(d-a,b-a))<=0 && 40 sgn(cross(a-c,d-c))*sgn(cross(b-c,d-c))<=0; 41 } 42 Point segcross(Point a,Point b,Point c,Point d){ 43 double u = cross(b-a,c-a) , v = cross(a-b,d-b); 44 return (Point){ (c.x*v+d.x*u)/(u+v) , (c.y*v+d.y*u)/(u+v)}; 45 } 46 vector<Point> Minke(vector<Point> pa,vector<Point> pb){ 47 int na = pa.size(),nb = pb.size(); 48 // 49 int ida = 0, idb = 0; 50 for(int i = 0;i<na;++i) if(pa[i] < pa[ida]) ida = i; 51 for(int i = 0;i<nb;++i) if(pb[i] < pb[idb]) idb = i; 52 vector<Point> ta , tb; 53 for(int i = 0;i<na;++i){ 54 ta.push_back(pa[ida]); 55 ida = (ida+1)%na; 56 } 57 for(int i = 0;i<nb;++i){ 58 tb.push_back(pb[idb]); 59 idb = (idb+1)%nb; 60 } 61 pa = ta; pb = tb; 62 63 for(int i = 0 ; i < na-1;++i) va[i] = pa[i+1] - pa[i]; 64 va[na-1] = pa[0] - pa[na-1]; 65 for(int i = 0 ; i < nb-1;++i) vb[i] = pb[i+1] - pb[i]; 66 vb[nb-1] = pb[0] - pb[nb-1]; 67 vector<Point> pc; 68 Point tem; 69 tem = pa[0] + pb[0]; 70 pc.push_back(tem); 71 int p1 = 0, p2 = 0; 72 while(p1 < na && p2 < nb){ 73 tem = tem + ( (cross(va[p1],vb[p2]) >= 0 ) ? va[p1++] : vb[p2++]); 74 pc.push_back(tem); 75 } 76 while(p1 < na){ 77 tem = tem + va[p1++]; 78 pc.push_back(tem); 79 } 80 while(p2 < nb){ 81 tem = tem + vb[p2++]; 82 pc.push_back(tem); 83 } 84 // 85 return pc; 86 } 87 void init(){ 88 double xmi = a[n+1][0].x,ymi = a[n+1][0].y,xmx = xmi , ymx = ymi; 89 for(auto it : a[n+1]){ 90 xmi = min(xmi,it.x) , xmx = max(xmx,it.x); 91 ymi = min(ymi,it.y) , ymx = max(ymx,it.y); 92 } 93 xl -= xmi; xr -= xmx; 94 yl -= ymi; yr -= ymx; 95 96 for(int i = 0;i<a[n+1].size();++i){ 97 a[n+1][i].x = -a[n+1][i].x; 98 a[n+1][i].y = -a[n+1][i].y; 99 } 100 for(int i = 1;i<=n;++i){ 101 b[i] = Minke(a[n+1],a[i]); 102 } 103 for(int i = 1;i<=n;++i){ 104 mi[i] = b[i][0].x , mx[i] = mi[i]; 105 for(auto it : b[i]){ 106 mi[i] = min(mi[i],it.x); 107 mx[i] = max(mx[i],it.x); 108 } 109 } 110 X[++cnt] = xl; 111 X[++cnt] = xr; 112 for(int i = 1;i<=n;++i){ 113 for(auto it : b[i]){ 114 if(it.x < xl + eps || it.x > xr - eps ) continue; 115 X[++cnt] = it.x; 116 } 117 } 118 for(int i = 1;i<=n;++i){ 119 for(int j = i+1;j<=n;++j){ 120 for(int ii = 0;ii<b[i].size()-1;++ii){ 121 for(int jj = 0;jj<b[j].size()-1;++jj){ 122 Point s1 = b[i][ii] , e1 = b[i][ii+1]; 123 Point s2 = b[j][jj] , e2 = b[j][jj+1]; 124 if( sgn(cross(e1 - s1,s2 - s1) ) == 0 && sgn(cross(e1 - s1,e2 - s1) == 0 ) ) continue; 125 if( insert(s1,e1,s2,e2) ){ 126 Point tem = segcross(s1,e1,s2,e2); 127 if(tem.x < xl + eps || tem.x > xr - eps ) continue; 128 X[++cnt] = tem.x; 129 } 130 } 131 } 132 } 133 } 134 rec[0] = (Point){xl,yl}; rec[1] = (Point){xr,yl}; 135 rec[2] = (Point){xr,yr}; rec[3] = (Point){xl,yr}; rec[4] = rec[0]; 136 for(int i = 1;i<=n;++i){ 137 for(int j = 0;j<b[i].size()-1;++j){ 138 for(int k = 0;k<4;++k){ 139 if(insert(b[i][j],b[i][j+1],rec[k],rec[k+1])){ 140 Point res = segcross(b[i][j],b[i][j+1],rec[k],rec[k+1]); 141 X[++cnt] = res.x; 142 } 143 } 144 } 145 } 146 sort(X+1,X+1+cnt); 147 } 148 void crosspol(vector<Point> p, double xx , vector< pair<double,int> >& Y){ 149 Point le = (Point){xx,-inf} , ri = (Point){xx,inf}; 150 vector<double> vec; 151 for(int j = 0;j<p.size()-1;++j){ 152 if( sgn( p[j].x - le.x) == 0 && sgn(p[j+1].x - le.x) == 0){ 153 vec.push_back(p[j].y); 154 vec.push_back(p[j+1].y); 155 } 156 else{ 157 if(insert(le,ri,p[j],p[j+1]) ){ 158 Point tem = segcross(le,ri,p[j],p[j+1]); 159 vec.push_back(tem.y); 160 } 161 } 162 } 163 sort(vec.begin(),vec.end()); 164 if(vec.size() < 2) return; 165 double ya = vec[0] , yb = vec[vec.size()-1]; 166 Y.push_back({max(ya,yl),1}); Y.push_back({min(yb,yr),-1}); 167 } 168 double solve_len(vector< pair<double,int> > Y){ 169 sort(Y.begin(),Y.end()); 170 int num = 0; 171 double res = 0; 172 double las; 173 for(auto it : Y){ 174 if(num==0 && it.second == 1){ 175 las = it.first; 176 ++num; 177 continue; 178 } 179 num += it.second; 180 if(num == 0 && it.second == -1) res += (it.first - las); 181 } 182 return res; 183 } 184 void solve(){ 185 double ans = 0; 186 for(int i = 1;i<cnt;++i){ 187 vector< pair<double,int> > Yl,Yr; 188 for(int j = 1;j<=n;++j){ 189 if(mx[j] < X[i] + eps || mi[j] > X[i+1] - eps ) continue; 190 crosspol(b[j],X[i],Yl); 191 crosspol(b[j],X[i+1],Yr); 192 } 193 double le = solve_len(Yl) , ri = solve_len(Yr); 194 ans += (ri + le) * (X[i+1] - X[i]) * 0.5; 195 } 196 printf("%.3f\n",(xr-xl)*(yr-yl) - ans); 197 return; 198 } 199 int main(){ 200 int T; scanf("%d",&T); 201 for(int cas = 1;cas <= T;++cas){ 202 cnt = 0; 203 printf("Case #%d: ",cas); 204 scanf("%d",&n); 205 for(int i = 1;i<=n+1;++i){ 206 a[i].clear(); 207 int m; scanf("%d",&m); 208 for(int j = 1;j<=m;++j){ 209 Point tem; 210 scanf("%lf %lf",&tem.x,&tem.y); 211 a[i].push_back(tem); 212 } 213 } 214 scanf("%lf %lf %lf %lf",&xl,&yl,&xr,&yr); 215 init(); 216 solve(); 217 } 218 return 0; 219 } 220 /* 221 6 222 1 223 3 224 0 0 225 10 0 226 10 10 227 4 228 0 0 229 1 0 230 1 1 231 0 1 232 0 0 10 10 233 */
2020.4.19 第九题:http://codeforces.com/group/uVAsoW2Jkj/contest/276618
这是我们的训练赛。 I 题是计几,题意:空间给你两个三角形,判断两个三角形最短距离。
由于我没有三维计几板子,然后直接炸裂,被迫写了个三分,(因为答案肯定是一个边和一个面距离,所以三分边,转换成点到三角形距离,然后T成傻逼了,结束之后队友爬山算法加各种优化帮我2.8s卡过去了)。
正解:先判断三角形是否相交,相交就0,否则就是顶点到另一个三角形距离,以及线段距离,这个很显然。然后就是上板子的事情了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-7 4 const double pi = acos(-1.0); 5 int sgn(double x){ 6 if( fabs(x) < eps ) return 0; 7 if( x > 0 ) return 1; 8 return -1; 9 } 10 struct Point{ 11 double x,y,z; 12 Point operator - (const Point& b)const{ 13 return (Point){x-b.x,y-b.y,z-b.z}; 14 } 15 Point operator + (const Point& b)const{ 16 return (Point){x+b.x,y+b.y,z+b.z}; 17 } 18 Point operator * (const double& b)const{ 19 return (Point){b*x,b*y,b*z}; 20 } 21 Point operator / (const double& b)const{ 22 return (Point){x/b,y/b,z/b}; 23 } 24 double len(){return sqrt(x*x+y*y+z*z); } 25 }T1[4],T2[4]; 26 Point cross(Point a,Point b){ 27 double xx = a.y*b.z-b.y*a.z; 28 double yy = a.x*b.z-b.x*a.z; 29 double zz = a.x*b.y-b.x*a.y; 30 return (Point){xx,-yy,zz}; 31 } 32 double dot(Point a,Point b){ 33 return a.x*b.x + a.y*b.y + a.z*b.z; 34 } 35 36 bool SameSide(Point p1,Point p2,Point a,Point b){ 37 return dot(cross(b - a, p1 - a), cross(b - a, p2 - a)) - eps >= 0; 38 } 39 bool PointInTri(Point P, Point P0, Point P1,Point P2){ 40 return SameSide(P, P0, P1, P2) && SameSide(P, P1, P0, P2) && SameSide(P, P2, P0, P1); 41 } 42 43 // 三角形P0P1P2是否和线段AB相交 44 bool TriSegIntersection(Point P0, Point P1, Point P2, Point A, Point B, Point & P){ 45 Point n = cross(P1 - P0, P2 - P0); 46 if(abs(dot(n, B - A)) <= eps) return false; // 线段A-B和平面P0P1P2平行或共面 47 else // 平面A和直线P1-P2有惟一交点 48 { 49 double t = dot(n, P0 - A) / dot(n, B - A); 50 if(t + eps < 0 || t - 1 - eps > 0) return false; // 不在线段AB上 51 P = A + (B - A) * t; // 交点 52 return PointInTri(P, P0, P1, P2); 53 } 54 } 55 //空间两三角形是否相交 56 bool TriTriIntersection(Point * T1, Point * T2){ 57 Point P; 58 for(int i = 0; i < 3; i++){ 59 if(TriSegIntersection(T1[0], T1[1], T1[2], T2[i], T2[(i + 1) % 3], P)) 60 { 61 return true; 62 } 63 64 if(TriSegIntersection(T2[0], T2[1], T2[2], T1[i], T1[(i + 1) % 3], P)) 65 { 66 return true; 67 } 68 } 69 70 return false; 71 } 72 73 74 //点到线段的距离 75 double point_to_seg(Point p, Point a, Point b){ 76 if((a-b).len()<eps) return (p - a).len(); //xiugai 77 Point v1 = b - a, v2 = p - a, v3 = p - b; 78 if(dot(v1, v2) + eps < 0) return v2.len(); 79 else{ 80 if(dot(v1, v3) - eps > 0) return v3.len(); 81 else return cross(v1, v2).len() / v1.len(); 82 } 83 } 84 85 86 double point_to_tri(Point p,Point a,Point b,Point c){ 87 if(PointInTri(p,a,b,c)){ 88 Point n = cross(b-a,c-a); 89 return fabs( dot( p-a ,n ) ) / n.len() ; 90 } 91 else{ 92 double res = point_to_seg(p,a,b); 93 res = min(res,point_to_seg(p,a,c)); 94 res = min(res,point_to_seg(p,b,c)); 95 return res; 96 } 97 } 98 99 // // 两线段距离 100 // double SegDis(Point a, Point b, Point c, Point d) { 101 // Point n = cross(a - b, c - d); 102 // if (sgn(n.len()) != 0) { 103 // n = n / n.len(); /// xiugai 104 // Point cc = GetPlaneProjection(c, a, n); 105 // Point dd = GetPlaneProjection(d, a, n); 106 // Point res; 107 // if (SegCross(a, b, cc, dd, res) == 1) 108 // return LineDis(a, b, c, d); 109 // } 110 // double ret = point_to_seg(a, c, d); 111 // ret = min(ret, point_to_seg(b, c, d)); 112 // ret = min(ret, point_to_seg(c, a, b)); 113 // ret = min(ret, point_to_seg(d, a, b)); 114 // return ret; 115 // } 116 117 //线段之间距离 118 double seg_to_seg(Point a,Point b,Point c,Point d){ 119 double res = min( point_to_seg(a,c,d) , point_to_seg(b,c,d) ); 120 res = min( res , min( point_to_seg(c,a,b) , point_to_seg(d,a,b) ) ); 121 Point normal = cross(b-a, d-c); 122 double cp1 = dot(normal, cross( d-c, a-c) ); 123 double cp2 = dot(normal, cross( d-c, b-c) ); 124 double cp3 = dot(normal, cross( b-a, c-a) ); 125 double cp4 = dot(normal, cross( b-a, d-a) ); 126 if (cp1*cp2 < -eps && cp3*cp4 < -eps ) { 127 Point p1 = (b*cp1 - a*cp2) / (cp1-cp2); 128 Point p2 = (d*cp3 - c*cp4) / (cp3-cp4); 129 res = min(res, (p2-p1).len()); 130 } 131 return res; 132 } 133 134 int main(){ 135 int T; scanf("%d",&T); 136 while(T--){ 137 scanf("%lf %lf %lf %lf %lf %lf %lf %lf %lf",&T1[0].x,&T1[0].y,&T1[0].z,&T1[1].x,&T1[1].y,&T1[1].z,&T1[2].x,&T1[2].y,&T1[2].z); 138 scanf("%lf %lf %lf %lf %lf %lf %lf %lf %lf",&T2[0].x,&T2[0].y,&T2[0].z,&T2[1].x,&T2[1].y,&T2[1].z,&T2[2].x,&T2[2].y,&T2[2].z); 139 if(TriTriIntersection(T1,T2)){ 140 printf("0.000000000\n"); 141 continue; 142 } 143 double ans = 1e60; 144 for(int i = 0;i<3;++i) ans = min(ans,point_to_tri(T1[i],T2[0],T2[1],T2[2]) ); 145 for(int i = 0;i<3;++i) ans = min(ans,point_to_tri(T2[i],T1[0],T1[1],T1[2]) ); 146 for(int i = 0;i<3;++i){ 147 for(int j = 0;j<3;++j){ 148 ans = min(ans,seg_to_seg(T1[i],T1[(i+1)%3] , T2[j],T2[(j+1)%3])); 149 } 150 } 151 printf("%.9f\n",ans); 152 } 153 return 0; 154 }
2020.4.20 第十题:cf1218B
这题就是给你n个仓库(凸多边形),原点有个镭射眼,可以透过一面墙看到仓库,问你看到仓库的最大面积是多少。
这题其实就直接是扫面线,因为两个线段的相对位置不会因为扫描线改变而改变,所以我们用set来维护靠近原点最近的线段,然后每次往set里面加减线段,找前两个线段统计答案就好。
这题不知道为什么一定要用long double 才能过。。。。。卡了很久,,,,
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 5e4+8; 4 #define double long double 5 const double eps = 1e-9; 6 const double pi = acos(-1.0); 7 const double inf = 1e6; 8 int cnt; 9 int segcnt = 0; 10 double curang; 11 struct Point{ 12 double x,y; 13 double corss(Point b)const{ 14 return x * b.y - b.x * y; 15 } 16 Point operator - (const Point& b)const{ 17 return (Point){x-b.x,y-b.y}; 18 } 19 double len(){return sqrt(x*x+y*y);} 20 }; 21 Point segcross(Point a,Point b,Point c,Point d,bool& ok ){ 22 double u = (b-a).corss(c-a) , v = (a-b).corss(d-b); 23 if( fabs(u+v) < eps){ 24 ok = 0; 25 Point tem = {-1,-1}; 26 return tem; 27 } 28 ok = 1; 29 return (Point){ (c.x*v + d.x*u)/(u+v) , (c.y*v + d.y*u)/(u+v) }; 30 } 31 struct Seg{ 32 Point a,b; 33 int id; 34 bool operator < (const Seg& s)const{ 35 bool ok1 ,ok2; 36 Point c = (Point){cos(curang) , sin(curang)}; 37 Point o = (Point){0,0}; 38 Point cr1 = segcross(a,b,o,c,ok1); 39 Point cr2 = segcross(s.a,s.b,o,c,ok2); 40 // if( ok1 == 0 || ok2 == 0 ) return id < s.id; 41 // assert(ok1 && ok2); 42 if( fabs( cr1.len() - cr2.len() ) < eps ) return id < s.id; 43 return cr1.len() < cr2.len(); 44 } 45 }; 46 vector<Point> pol[N]; 47 double ang[N]; 48 vector<int> add[N],del[N]; 49 Seg seg[N]; 50 51 void add_seg(Point a,Point b){ 52 double anga = atan2(a.y,a.x) , angb = atan2(b.y,b.x); 53 if( fabs(anga - angb) < eps ) return; 54 if( anga > angb + eps ){ 55 swap(anga,angb); 56 swap(a,b); 57 } 58 int pa = lower_bound(ang+1,ang+1+cnt,anga) - ang; 59 int pb = lower_bound(ang+1,ang+1+cnt,angb) - ang; 60 61 ++segcnt; 62 seg[segcnt] = (Seg){a,b,segcnt}; 63 if( angb - anga > pi + eps ){ 64 // Point c = segcross(a,b,(Point){-inf,0},(Point){inf,0}); 65 if( fabs(anga + pi) > eps){ 66 // ++segcnt; 67 // seg[segcnt] = (Seg){c,a,segcnt}; 68 add[1].push_back(segcnt); 69 del[pa].push_back(segcnt); 70 } 71 72 if( fabs( angb - pi ) > eps){ 73 // ++segcnt; 74 // seg[segcnt] = (Seg){b,c,segcnt}; 75 add[pb].push_back(segcnt); 76 del[cnt].push_back(segcnt); 77 } 78 } 79 else{ 80 add[pa].push_back(segcnt); 81 del[pb].push_back(segcnt); 82 } 83 } 84 double area(Point a,Point b,Point c,Point d){ 85 double res = 0; 86 res += (b-a).corss(c-a); 87 res += (c-a).corss(d-a); 88 if(res < -eps ) res = -res; 89 return res * 0.5; 90 } 91 void solve(){ 92 set<Seg> st; 93 double ans = 0; 94 curang = 0; 95 for(int i = 1;i<cnt;++i){ 96 for(auto it : del[i] ) st.erase(seg[it]); 97 curang = ( ang[i] + ang[i+1] ) / 2.0; 98 for(auto it : add[i]) st.insert(seg[it]); 99 if((int)st.size() >= 2){ 100 // assert((int)st.size()>=2); 101 Seg s1,s2; 102 auto it = st.begin(); s1 = (*it); 103 ++it; s2 = (*it); 104 Point c1 = (Point){inf*cos(ang[i]) , inf * sin(ang[i])}; 105 Point c2 = (Point){inf*cos(ang[i+1]) , inf * sin(ang[i+1])}; 106 Point o = (Point){0,0}; 107 bool ok1,ok2,ok3,ok4; 108 Point a1 = segcross(o,c1,s1.a,s1.b,ok1); 109 Point a2 = segcross(o,c1,s2.a,s2.b,ok2); 110 Point a3 = segcross(o,c2,s2.a,s2.b,ok3); 111 Point a4 = segcross(o,c2,s1.a,s1.b,ok4); 112 // assert(ok1 && ok2 && ok3 && ok4); 113 // if( ok1 == 0 || ok2 == 0 || ok3 == 0 || ok4 == 0) continue; 114 ans += area(a1,a2,a3,a4); 115 } 116 } 117 // printf("%.12f",ans); 118 cout<<ans<<endl; 119 } 120 int main(){ 121 int n; cin>>n; 122 for(int i = 1;i<=n;++i){ 123 int m; cin>>m; 124 Point tem; 125 for(int j = 1;j<=m;++j){ 126 // scanf("%lf %lf",&tem.x,&tem.y); 127 cin>>tem.x>>tem.y; 128 // cerr<<tem.x<<" "<<tem.y<<" !"<<endl; 129 pol[i].push_back(tem); 130 ang[++cnt] = atan2(tem.y,tem.x); 131 } 132 pol[i].push_back(pol[i][0]); 133 } 134 ang[++cnt] = -pi; ang[++cnt] = pi; 135 sort(ang+1,ang+1+cnt); 136 cnt = unique(ang+1,ang+1+cnt) - ang - 1; 137 138 for(int i = 1;i<=n;++i){ 139 for(int k = 0;k<pol[i].size()-1;++k){ 140 add_seg(pol[i][k],pol[i][k+1]); 141 } 142 } 143 144 solve(); 145 return 0; 146 }
2020.4.22 第十一题:hdu4805
这题卡了我一天了,这题不知道为什么会挂,然后对着过了的代码改,发现好像是std写挂了???不知道,反着这题挺奇怪的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-9 4 // #define double long double 5 const int N = 1e5+9; 6 bool vis[N]; 7 int sgn(double x){ 8 if( fabs(x) < eps) return 0; 9 if( x < 0 ) return -1; 10 return 1; 11 } 12 // double ans,stlen; 13 struct Point{ 14 double x,y,z; 15 Point operator - (const Point& b)const{ 16 return (Point){x-b.x,y-b.y,z-b.z}; 17 } 18 Point operator * (const double& b)const{ 19 return (Point){x*b,y*b,z*b}; 20 } 21 Point operator / (const double& b)const{ 22 return (Point){x/b,y/b,z/b}; 23 } 24 Point operator + (const Point& b)const{ 25 return (Point){x+b.x,y+b.y,z+b.z}; 26 } 27 double len(){return sqrt(x*x+y*y+z*z);} 28 double len2(){return x*x+y*y+z*z;} 29 }S,T,dir,Tri[N][5]; 30 int n; 31 Point cross(Point A, Point B) { 32 return Point{A.y * B.z - A.z * B.y, A.z * B.x - A.x * B.z, A.x * B.y - A.y * B.x}; 33 } 34 double dot(Point a,Point b){ 35 return a.x * b.x + a.y * b.y + a.z* b.z; 36 } 37 //点是否在线段上(包括顶点) 38 bool Point_on_Seg(Point p,Point a,Point b){ 39 return sgn( dot(a-p,b-p) ) <=0 && sgn( cross(a-p,b-p).len() ) == 0; 40 } 41 //点是否在直线上 42 bool Point_on_Line(Point p,Point a,Point b){ 43 return cross(p-a,b-a).len() < eps; 44 } 45 bool intersect(Point a,Point b,Point c,Point d){ 46 return sgn( dot( cross(b-a,c-a) , cross(b-a,d-a) ) ) <= 0 && sgn( dot( cross(d-c,a-c) ,cross(d-c,b-c))) <= 0; 47 } 48 bool parallel(Point a,Point b,Point c,Point d){ 49 return sgn( cross(a-b,c-d).len() ) == 0; 50 } 51 //直线相交判定 0:不相交(平行); 1:规范相交; 2:非规范相交(重合); 3:异面不相交 52 int LineCross(Point a, Point b, Point c, Point d, Point &res) { 53 // Point n = cross(b - a, d - c); 54 Point n = dir; 55 if ( sgn(dot( n, c - a) ) != 0) return 3; 56 if (sgn(n.len()) == 0) { 57 if (sgn(cross(a - b, c - b).len()) == 0) return 2; 58 return 0; 59 } 60 n = d + n; 61 Point f = cross(d - c, n - c); 62 double t = dot(f, c - a) / dot(f, b - a); 63 res = a + (b - a) * t; 64 return 1; 65 } 66 67 // 线段相交判定 0:不相交; 1:规范相交; 2:非规范相交(包含端点),3:重合 68 int SegCross(Point a, Point b, Point c, Point d,Point &res) { 69 int k = LineCross(a, b, c, d, res); 70 if (k == 0 || k == 3) return 0; 71 if (k == 1) { 72 double d1 = dot(a - res, b - res); 73 double d2 = dot(c - res, d - res); 74 if (d1 < -eps && d2 < -eps ) return 1; 75 if ( (sgn(d1) == 0 && d2 < eps) || (sgn(d2) == 0 && d1 < eps) ) return 2; //包含端点 76 return 0; 77 } 78 if (dot(a - c, b - c) <= 0 || dot(a - d, b - d) <= 0 79 || dot(c - a, d - a) <= 0 || dot(c - b, d - b) <= 0) 80 return 3; 81 return 0; 82 } 83 // p1和p2是否在线段a-b的同侧,(p1,p2,a,b 同一平面) 84 // p1,p2,a,b不同平面时,表示p1ab,p2ab两个半平面夹角是否锐角 85 bool SameSide(Point p1,Point p2,Point a,Point b){ 86 return dot(cross(b - a, p1 - a), cross(b - a, p2 - a)) > -eps ; 87 } 88 // 点P在三角形P0, P1, P2中 (p,p0,p1,p2 同一平面) 89 // p和p0p1,p2不同一面时,判断点p是否在p0,p1,p2形成的三角形柱形内 90 bool PointInTri(Point P,Point P0,Point P1,Point P2){ 91 return SameSide(P, P0, P1, P2) && SameSide(P, P1, P0, P2) && SameSide(P, P2, P0, P1); 92 } 93 bool In(Point p,Point a,Point b,Point c){ 94 if( parallel(a,b,a,c) ){ 95 return Point_on_Seg(p,a,b) || Point_on_Seg(p,a,c) || Point_on_Seg(p,b,c); 96 } 97 Point ta = cross(b-a,p-a); 98 Point tb = cross(c-b,p-b); 99 Point tc = cross(a-c,p-c); 100 return sgn(dot(ta,tb))>=0 && sgn(dot(tb,tc))>=0 && sgn(dot(ta,tc))>=0; 101 } 102 // 点在平面上的投影 103 Point point_plane_projection(Point p, Point p0, Point n) { 104 double d = dot(p - p0, n) ; 105 return p - n * d / n.len2(); 106 } 107 double judge( int id){ 108 if( cross(Tri[id][0]-S,T-S).len() < eps ){ 109 double tmp = 0; 110 for(int i = 0;i<3;++i){ 111 Point a = Tri[id][i] , b = Tri[id][(i+1)%3]; 112 if( Point_on_Seg(T,a,b) ){ 113 if( Point_on_Seg(S,a,b) ) tmp = max(tmp,(S-T).len()); 114 if( Point_on_Seg(a,S,T) ) tmp = max(tmp,(a-T).len()); 115 if( Point_on_Seg(b,S,T) ) tmp = max(tmp,(b-T).len()); 116 } 117 if( Point_on_Seg(a,S,T) && Point_on_Seg(b,S,T) ) return (b-a).len(); 118 } 119 return tmp; 120 } 121 return 0; 122 } 123 124 Point line_make_point(Point ua,Point ub,Point va,Point vb){ 125 double t = ( (ua.x-va.x)*(va.y-vb.y)-(ua.y-va.y)*(va.x-vb.x) ) 126 / ((ua.x-ub.x)*(va.y-vb.y)-(ua.y-ub.y)*(va.x-vb.x)); 127 return ua + (ub-ua)*t; 128 } 129 double solve(Point s,Point t,Point a,Point b){ 130 if( !parallel(s,t,a,b) ){ 131 if( !intersect(s,t,a,b) ) return 0; 132 Point tem = line_make_point(s,t,a,b); 133 return (tem-t).len(); 134 } 135 if( sgn(cross(t-s,b-s).len() )!= 0 ) return 0; 136 if( Point_on_Seg(t,a,b) ){ 137 double tem = 0; 138 if( Point_on_Seg(a,s,t) ) tem = max(tem,(a-t).len()); 139 if( Point_on_Seg(b,s,t) ) tem = max(tem,(b-t).len()); 140 return tem; 141 } 142 if( Point_on_Seg(a,s,t) && Point_on_Seg(b,s,t)) return (b-a).len(); 143 return 0; 144 } 145 146 void work(){ 147 double ans = 0; 148 for(int i = 1;i<=n;++i){ 149 bool Sin = In(S,Tri[i][0],Tri[i][1],Tri[i][2]); 150 bool Tin = In(T,Tri[i][0],Tri[i][1],Tri[i][2]); 151 if( Sin && Tin ){ 152 ans += (T-S).len(); 153 // continue; 154 } 155 if( Sin ){ 156 swap(S,T); 157 swap(Sin,Tin); 158 } 159 if(vis[i]){ 160 // if( Sin && Tin ) ans += (T-S).len(); 161 ans += judge(i); 162 continue; 163 } 164 else if( Tin ){ 165 if( Sin && Tin ) continue; 166 double tem = 0; 167 for(int k = 0;k<3;++k){ 168 Point a = Tri[i][k] , b = Tri[i][(k+1)%3]; 169 Point res; 170 int tet = SegCross(a,b,S,T,res); 171 if( tet == 1 || tet == 2){ 172 SegCross(a,b,S,T,res); 173 tem = max(tem,(res-T).len()); 174 } 175 } 176 ans += tem; 177 } 178 else{ 179 if( Sin && Tin ) continue; 180 vector<Point> res; 181 for(int k = 0;k<3;++k){ 182 Point a = Tri[i][k] , b = Tri[i][(k+1)%3]; 183 Point tem; 184 int tet = SegCross(S,T,a,b,tem); 185 if( tet == 1 || tet == 2) res.push_back(tem); 186 } 187 if( res.size() ) ans += (res[1] - res[0]).len(); 188 } 189 } 190 ans /= (T-S).len(); 191 printf("%.8f\n",ans); 192 } 193 int main(){ 194 while(~scanf("%d",&n)){ 195 scanf("%lf %lf %lf",&S.x,&S.y,&S.z); 196 scanf("%lf %lf %lf",&T.x,&T.y,&T.z); 197 scanf("%lf %lf %lf",&dir.x,&dir.y,&dir.z); 198 for(int i = 1;i<=n;++i){ 199 vis[i] = 0; 200 scanf("%lf %lf %lf",&Tri[i][0].x,&Tri[i][0].y,&Tri[i][0].z); 201 scanf("%lf %lf %lf",&Tri[i][1].x,&Tri[i][1].y,&Tri[i][1].z); 202 scanf("%lf %lf %lf",&Tri[i][2].x,&Tri[i][2].y,&Tri[i][2].z); 203 if( sgn( dot( cross(Tri[i][1] - Tri[i][0],Tri[i][2]-Tri[i][0]) , dir) ) == 0 ) vis[i] = 1; 204 for(int j = 0;j<3;++j){ 205 Tri[i][j] = point_plane_projection(Tri[i][j],S,dir); 206 } 207 } 208 if( sgn( cross(T-S,dir).len() ) == 0 ){ 209 double ans = 0; 210 for(int i = 1;i<=n;++i){ 211 if(In(S,Tri[i][0],Tri[i][1],Tri[i][2]) ) ans = ans+1; 212 } 213 printf("%.8f\n",ans); 214 continue; 215 } 216 T = point_plane_projection(T,S,dir); 217 work(); 218 } 219 return 0; 220 }
2020.4.23 第十二题 :codeforces1146H
题意:二维平面给你300个点,没有三点共线,问你能构造多少个五角星(不一定规则五角星)。
这题开始想了个假算法,后来就秒了。 首先五角星转换为严格凸五边形。然后dp[i] [j] [k] 代表 i 一直链接到 j 其中有 k 个线段。最后直接 sigama dp[i][i][5]即可。
我们只需要把所有线段极角排序,然后枚举每一个线段,每个线段(s,t)加进来的时候,直接把 dp[s] 转移到 dp[t] 即可。
正确性:显然,一个五边形是能够从最顶端的那条边一直转移过来的,那么会不会统计多了呢?不会!因为假如一个虚假的五边形被统计,比如说四边形加一条重边,画个图就知道是不会统计进答案的。
总结:一开始我是打算分开每个点来统计答案,但是这样有个问题就是保证不了凸性,也就是线段的斜率不保证单调,这就很难搞。而且枚举每个点的话,只能固定起点,终点没固定的话就可能会绕回来导致重边,所以我的dp一定是有两维分别是起点终点。然后我对于所有线段排序,其实就是从全局来考虑,这样问题会化简很多,判断最大最小面积三角形,以及四边形面积存在性也用到过这个思想。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 #define eps 1e-8 5 const int N = 300+9; 6 struct Point{ 7 ll x,y; 8 int id; 9 Point operator - (const Point& b)const{ 10 return (Point){x-b.x,y-b.y}; 11 } 12 double ang(){return atan2(y,x);} 13 ll cross(Point b){ 14 return x*b.y - b.x*y; 15 } 16 }p[N]; 17 ll dp[N][N][7]; 18 struct Seg{ 19 Point s,t; 20 bool operator < (const Seg& b)const{ 21 double anga = (t-s).ang() , angb = (b.t-b.s).ang(); 22 if( fabs(anga - angb) > eps) return anga < angb; 23 return (t-s).cross(b.t-b.s) >0; 24 } 25 }seg[N*N]; 26 int main(){ 27 int n; 28 int cnt = 0; 29 scanf("%d",&n); 30 for(int i = 1;i<=n;++i){ 31 scanf("%lld %lld",&p[i].x,&p[i].y); 32 p[i].id = i; 33 } 34 for(int i = 1;i<=n;++i){ 35 for(int j = i + 1;j<=n;++j){ 36 seg[++cnt] = (Seg){p[i],p[j]}; 37 seg[++cnt] = (Seg){p[j],p[i]}; 38 } 39 } 40 sort(seg+1,seg+1+cnt); 41 for(int i = 1;i<=cnt;++i){ 42 int tid = seg[i].t.id , sid = seg[i].s.id; 43 dp[sid][tid][1] += 1; 44 for(int j = 1;j<=n;++j){ 45 for(int k = 2;k<=5;++k){ 46 dp[sid][j][k] += dp[tid][j][k-1]; 47 } 48 } 49 } 50 ll ans = 0; 51 for(int i = 1;i<=n;++i) ans += dp[i][i][5]; 52 printf("%lld",ans); 53 }
接下来几天先放放计几,打算训练一下图论以及cf题,提高一下个人能力。
(upd : 还是打算把cf2900以下的,题号500+的刷一波吧)
2020.4.24 第十三题:codeforces598F
题意:给你一个1000个点的简单多边形,给你100条直线,问你每条直线和多边形相交的部分的长度。
edu1 的 F 题。。。。。
emm 思路很简单,就是对于每条直线,暴力求和简单多边形的交点,然后再统计,就是O(nm)的。 但是有一个很烦的问题就是,你怎么统计。
很容易想到射线法判断点是否在凸包里面的方法,就是每一个交点,我们就相当于 凸包内/ 凸包外 的一个状态转化,所以很容易求出来。但是有个问题就是我射线法只是判断点是不是在凸包内,所以我遇到平行线段就直接不管。但是这里不行啊!那如果平行的直接加进答案?不行!会重复,思考了一个晚上,假了一个晚上,早上起来实在没想到别的了,于是乎打了一个扫描线居然就过了。。
总体做法:先对图旋转,问题变成多边形和x轴相交线段,然后我们开一个 add 数组记录添加的线段,对于x轴上的线段直接丢进add,其他的情况也丢进add,最后add进行扫描线,这样就不会重复了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-8 4 const int N = 1000+9; 5 struct Point{ 6 double x,y; 7 Point operator - (const Point& b)const{ 8 return (Point){x-b.x,y-b.y}; 9 } 10 Point operator * (const double& b)const{ 11 return (Point){x*b,y*b}; 12 } 13 Point operator / (const double& b)const{ 14 return (Point){x/b,y/b}; 15 } 16 Point turn(const double& k){ 17 return (Point){ x*cos(k)-y*sin(k),x*sin(k)+y*cos(k)}; 18 } 19 double cross(Point a,Point b){ 20 return (a.x-x) * (b.y-y) - (b.x-x)*(a.y - y); 21 } 22 bool operator < (const Point& b)const{ 23 if( fabs(x-b.x) < eps ) return y < b.y; 24 return x < b.x; 25 } 26 27 }p[N],tp[N]; 28 double X[N*3],Xadd[N*3]; 29 // struct Node{ 30 // int num; 31 // double x1,x2; 32 // bool operator < (const Node& b)const{ 33 // return x1 < b.x1; 34 // } 35 // }; 36 int sgn(double x){ 37 if( fabs(x) < eps ) return 0; 38 if( x < 0) return -1; 39 return 1; 40 } 41 double dis(Point a,Point b){ 42 return sqrt((a.x - b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)); 43 } 44 bool insert(Point a,Point b,Point c,Point d){ 45 // cerr<<c.x<<" "<<c.y<<" "<<d.x<<" "<<d.y<<" "<<sgn(a.cross(b,d))<<" "<<a.cross(b,d)<<"!!"<<endl; 46 // if( sgn( a.cross(b,d)) == 0 ) return 1; 47 return sgn( a.cross(b,c) ) * sgn( a.cross(b,d) ) < 0; 48 } 49 Point LineCross(Point a,Point b,Point c,Point d){ 50 double u = a.cross(b,c), v = b.cross(a,d); 51 return (Point){ (c.x*v + d.x*u)/(u+v) , (c.y*v + d.y*u)/(u+v) }; 52 } 53 int n; 54 void init(){ 55 int cnt = 0; 56 for(int i = 1;i<=n+1;++i){ 57 if( cnt>=2 && sgn(p[i].cross(tp[cnt],tp[cnt-1])) == 0 ) --cnt; 58 tp[++cnt] = p[i]; 59 } 60 for(int i = 1;i<=cnt;++i) p[i] = tp[i]; 61 n = cnt-1; 62 } 63 void solve(Point s,Point t){ 64 double deta = atan2(t.y-s.y,t.x-s.x); 65 for(int i = 1;i<=n+1;++i){ 66 tp[i] = (p[i]-s).turn(-deta); 67 // cerr<<i<<" "<<tp[i].x<<" "<<tp[i].y<<endl; 68 } 69 t = (Point){-1000000,0}; 70 s = (Point){1000000,0}; 71 int cnt = 0; 72 // vector<Node> vec; 73 vector< pair<double,double> > add; 74 vector<double> tem; 75 for(int i = 1;i<=n;++i){ 76 Point a = tp[i] , b = tp[i+1]; 77 if( fabs(a.y) < eps && fabs(b.y)<eps ){ 78 add.push_back({min(a.x,b.x) , max(a.x,b.x)}); 79 X[++cnt] = a.x; X[++cnt] = b.x; 80 continue; 81 } 82 if( a.y > b.y + eps ) swap(a,b); 83 if( fabs(b.y) < eps || ( fabs(a.y) > eps && insert(s,t,a,b) ) ){ 84 Point pt = LineCross(s,t,a,b); 85 // cerr<<i<<" "<<tem.x<<" "<<tem.y<<endl; 86 // tem.x1 = pt.x; tem.num = 1; 87 // vec.push_back(tem); 88 tem.push_back(pt.x); 89 X[++cnt] = pt.x; 90 } 91 } 92 double ans = 0; 93 sort(X+1,X+1+cnt); 94 cnt = unique(X+1,X+1+cnt) - X - 1; 95 sort(tem.begin(),tem.end()); 96 for(int i = 0;i<tem.size();i+=2) add.push_back({tem[i],tem[i+1]}); 97 for(int i = 1;i<=cnt;++i) Xadd[i] = 0; 98 for(auto it : add){ 99 int pl = lower_bound(X+1,X+1+cnt,it.first) - X; 100 int pr = lower_bound(X+1,X+1+cnt,it.second) - X; 101 Xadd[pl] += 1; Xadd[ pr ] -= 1; 102 } 103 int num = 0; 104 for(int i = 1;i<cnt;++i){ 105 num += Xadd[i]; 106 if( num ) ans += X[i+1] - X[i]; 107 } 108 printf("%.15f\n",ans); 109 } 110 int main(){ 111 int m; 112 scanf("%d",&n); 113 scanf("%d",&m); 114 for(int i = 1;i<=n;++i) scanf("%lf %lf",&p[i].x,&p[i].y); 115 p[n+1] = p[1]; 116 init(); 117 for(int i = 1;i<=m;++i){ 118 Point a,b; scanf("%lf %lf %lf %lf",&a.x,&a.y,&b.x,&b.y); 119 solve(a,b); 120 } 121 return 0; 122 } 123 /* 124 4 1 125 0 0 126 0 1 127 1 1 128 1 0 129 1 1 0 0 130 */
正解:多边形线段看作有向线段,和直线向量所成正向的线段+,反向线段 - 。 其中规范相交的权值是2,交在端点上权值是1 ,和直线平行的不管。 然后再 扫描线(和我的差不多,因为交正向相当于进入多边形,交反向相当于离开多边形,处理端点,射线法是只管高点,这里是让交点是端点的权值 = 1 ,因为不规范相交不会影响,所以权值设小点就没影响了。
1 // 2 // Created by TaoSama on 2016-01-19 3 // Copyright (c) 2015 TaoSama. All rights reserved. 4 // 5 #pragma comment(linker, "/STACK:1024000000,1024000000") 6 #include <algorithm> 7 #include <cctype> 8 #include <cmath> 9 #include <cstdio> 10 #include <cstdlib> 11 #include <cstring> 12 #include <iomanip> 13 #include <iostream> 14 #include <map> 15 #include <queue> 16 #include <string> 17 #include <set> 18 #include <vector> 19 20 using namespace std; 21 #define pr(x) cout << #x << " = " << x << " " 22 #define prln(x) cout << #x << " = " << x << endl 23 const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7; 24 25 #define double long double 26 const double EPS = 1e-8; 27 28 int sgn(double x) { 29 return x < -EPS ? -1 : x > EPS; 30 } 31 32 struct Point { 33 double x, y; 34 Point() {} 35 Point(double x, double y): x(x), y(y) {} 36 void read() {cin >> x >> y;} 37 Point operator-(const Point& p) { 38 return Point(x - p.x, y - p.y); 39 } 40 double operator*(const Point& p) { 41 return x * p.x + y * p.y; 42 } 43 double operator^(const Point& p) { 44 return x * p.y - y * p.x; 45 } 46 double length() {return hypotl(x, y);} 47 } poly[N]; 48 49 using Vec = Point; 50 51 double getIntersection(Point p, Vec v, Point q, Vec w) { 52 return (w ^ (p - q)) / (v ^ w); 53 } 54 55 int n, q; 56 57 double gao(Point p, Vec v) { 58 double ret = 0; 59 vector<pair<double, int> > pos; 60 for(int i = 1; i <= n; ++i) { 61 int s1 = sgn(v ^ (poly[i] - p)); 62 int s2 = sgn(v ^ (poly[i + 1] - p)); 63 if(s1 == s2) continue; //collinear or no intersection 64 double o = getIntersection(p, v, poly[i], poly[i + 1] - poly[i]); 65 if(s1 > s2) pos.push_back({o, s1 && s2 ? 2 : 1}); 66 else pos.push_back({o, s1 && s2 ? -2 : -1}); 67 } 68 sort(pos.begin(), pos.end()); 69 int flag = 0; 70 for(int i = 0; i + 1 < pos.size(); ++i) { 71 flag += pos[i].second; 72 if(flag) ret += pos[i + 1].first - pos[i].first; 73 } 74 return ret * v.length(); 75 } 76 77 int main() { 78 #ifdef LOCAL 79 freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin); 80 // freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout); 81 #endif 82 ios_base::sync_with_stdio(0); 83 84 cin >> n >> q; 85 for(int i = 1; i <= n; ++i) poly[i].read(); 86 poly[n + 1] = poly[1]; 87 while(q--) { 88 Point A, B; 89 A.read(); B.read(); 90 cout << fixed << setprecision(20) << gao(A, B - A) << '\n'; 91 } 92 return 0; 93 }
2020.4.25 第十四题:codeforces611G
就是类似于旋转卡壳的一道题,记住多个点和一个点的叉积的和是可以变成 多个点的和的虚点 和 这个点的叉积,然后就是一个前缀和瞎搞就好了。这题很容易爆ll,然后注意面积不能取模。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 5e5+8; 5 const ll modd = 1e9+7; 6 struct Point{ 7 ll x,y; 8 Point operator - (const Point& b)const{ 9 return (Point){x-b.x,y-b.y}; 10 } 11 }p[N*2],all,tp[N*2]; 12 ll cross(Point a,Point b){ 13 return a.x*b.y - b.x*a.y; 14 } 15 ll a[N*2],sum[N*2],ssum[N*2]; 16 int n; 17 int main(){ 18 scanf("%d",&n); 19 for(int i = 1;i<=n;++i) scanf("%lld %lld",&p[i].x,&p[i].y); 20 reverse(p+1,p+1+n); 21 22 for(int i = 1;i<=n;++i) p[i+n] = p[i]; 23 for(int i = 1;i<2*n;++i) a[i] = cross(p[i],p[i+1]) % modd; 24 for(int i = 1;i<2*n;++i) sum[i] = (sum[i-1] + a[i])%modd; 25 for(int i = 1;i<2*n;++i) ssum[i] = (ssum[i-1] + sum[i])%modd; 26 27 ll tot = 0 , ans = 0; 28 all.x = 0 , all.y = 0; 29 for(int i = 1;i<=n;++i) tot += cross(p[i],p[i+1]); 30 double dtot = 1.0 * tot; 31 ll now = cross(p[1],p[2]); 32 ll cnt =0; 33 for(int l = 1,r=2;l<=n;++l){ 34 35 while( now + cross(p[r],p[r+1]) + cross(p[r+1],p[l]) < dtot/2 ){ 36 now += cross(p[r],p[r+1]); 37 ++r; 38 all.x = (all.x + p[r].x + modd )%modd; 39 all.y = (all.y + p[r].y + modd )%modd; 40 } 41 ll tem = ( (ssum[r-1] - ssum[l] - (r-l-1)*sum[l-1]%modd + modd)%modd + cross(all,p[l])%modd ) % modd; 42 ans = (ans + tem) % modd; 43 all.x = (all.x - p[l+2].x + modd) % modd; 44 all.y = (all.y - p[l+2].y + modd) % modd; 45 now -= cross(p[l],p[l+1]); 46 cnt += r-l-1; 47 } 48 tot = tot % modd; 49 ans = (cnt %modd * tot % modd - 2 * ans%modd + modd) % modd; 50 printf("%lld",ans); 51 return 0; 52 }
2020.4.26 第十五题:codeforces958E3
分治构造题。做法:对于分治的点,找到左下角的点,扫描线找到左边的百点和黑点相同就break出来,继续分分治。 首先,假如有解,我一定可以根据这个做法构造出来的。因为假如说左边黑点链接右面百点,那么我们肯定可以重新构造来使得左边白点等于黑点,这个画画图就好了。。。。那么我这样做能找到最终解么?肯定的,因为每次这样操作一定是保证了线段不交,然后每一次操作都是一定保证线段不交,最后答案就肯定是线段不交
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 1e4+9; 5 struct Point{ 6 int col,id; 7 int x,y; 8 bool operator < (const Point& b){ 9 if( x == b.x ) return y < b.y; 10 return x < b.x; 11 } 12 }p0,p[N*2]; 13 int cross(Point a,Point b,Point c){ 14 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 15 } 16 bool cmpang(Point a,Point b){ 17 return cross(p0,a,b) > 0; 18 } 19 int ans[N]; 20 void solve(int L,int R){ 21 int c = L; 22 for(int i = L;i<=R;++i) if(p[i]<p[c]) c = i; 23 swap(p[c],p[L]); 24 p0 = p[L]; 25 sort(p+L+1,p+R+1,cmpang); 26 int a1 = 0, a0 = 0; 27 int k = 1000000; 28 int mid = (L+R)>>1; 29 // cerr<<L<<" "<<R<<" LR"<<endl; 30 for(int i = L+1;i<= R ;++i){ 31 if( a1 == a0 && p[i].col != p0.col){ 32 k = i; 33 break; 34 // cerr<<"!!!"<<p0.id<<" "<<p[i].id<<"!!"<<endl; 35 // break; 36 } 37 if(p[i].col) ++a1; 38 else ++a0; 39 } 40 if(p0.col) ans[p[k].id] = p0.id; 41 else ans[p0.id] = p[k].id; 42 // cerr<<p0.x<<" "<<p0.y<<" "<<p[k].x<<" "<<p[k].y<<" !"<<endl; 43 // swap(p[L],p[k-1]); 44 if(L+1<k-1) solve(L+1,k-1); 45 if(k+1 < R) solve(k+1,R); 46 } 47 inline int read() { 48 char ch = getchar(); int x = 0, f = 1; 49 while(ch < '0' || ch > '9') { 50 if(ch == '-') f = -1; 51 ch = getchar(); 52 } while('0' <= ch && ch <= '9') { 53 x = x * 10 + ch - '0'; 54 ch = getchar(); 55 } return x * f; 56 } 57 int main(){ 58 59 int n = read(); 60 for(int i = 1;i<=n;++i){ 61 p[i].x = read(); 62 p[i].y = read(); 63 p[i].col = 0; p[i].id = i; 64 } 65 for(int i = 1;i<=n;++i){ 66 p[i+n].x = read(); 67 p[i+n].y = read(); 68 p[i+n].col = 1; p[i+n].id = i; 69 } 70 solve(1,2*n); 71 for(int i = 1;i<=n;++i) cout<<ans[i]<<"\n"; 72 }
2020.5.6 第十六题:codeforces962G
突然发现鸽了好多天了。最近忙着作业还有其他东西,计几就停了一下。
题意:给你一个只有水平和垂直线段的简单多边形,内部染成黑色,然后给你一个矩形,问矩形内有多少个黑色区域。
做法:这题假了好多次,然后卡了一下午总算过了。其实计几有个常用的套路就是线段有向,以逆时针为方向。那么顺着这个思路想,我们可以对原来的图形建立有向线段的图。多边形和矩形交的线段在图内。矩形边上的点也在图内。然后一个黑色区域是可以从一个点dfs回自身的。所以建完图之后跑一遍dfs即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 15000+9; 4 typedef long long ll; 5 struct Point{ 6 int x,y; 7 Point operator - (const Point& b)const{ 8 return (Point){x-b.x,y-b.y}; 9 } 10 }p[N],rec[7],Gp[N]; 11 bool vis[N*5]; 12 vector< pair<int,int> > edge_pt[5]; 13 map<pair<int,int> , int > mp; 14 vector<int> G[N]; 15 // int cross(Point a,Point b,Point c){ 16 // return (b.x-a.x)*(c.y-b.y) - (c.x-a.x) * (a.y-b.y); 17 // } 18 // int dot(Point a,Point b,Point c){ 19 // return (b.x-a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y); 20 // } 21 // bool onSeg(Point a,Point s,Point e){ 22 // return cross(a,s,e)==0 && dot(a,s,e) <= 0 ; 23 // } 24 int n; 25 int cnt = 0; 26 bool judge(){ 27 double xx = (rec[1].x + rec[3].x) * 0.5; 28 double yy = (rec[1].y + rec[3].y) * 0.5; 29 int cnt = 0; 30 // for(int i = 1;i<=4;++i){ 31 // if( onSeg(,rec[i],rec[i+1])) return 1; 32 // } 33 for(int i = 1;i<=n;++i){ 34 if( p[i].y == p[i+1].y) continue; 35 if( p[i].x < xx) continue; 36 int mi = min(p[i].y,p[i+1].y) , mx = max(p[i].y,p[i+1].y); 37 // cerr<<yy<<" "<<mi<<" "<<mx<<" !!"<<endl; 38 if( yy <= mi || yy > mx) continue; 39 ++cnt; 40 } 41 if( cnt&1 ) return 1; 42 return cnt&1; 43 } 44 void add(int xa,int ya,int xb,int yb){ 45 if( !mp[{xa,ya}] ) mp[{xa,ya}] = ++cnt; 46 if( !mp[{xb,yb}] ) mp[{xb,yb}] = ++cnt; 47 int aid = mp[{xa,ya}], bid = mp[{xb,yb}]; 48 G[aid].push_back(bid); 49 if( xa == rec[1].x ) edge_pt[1].push_back({ya,aid}); 50 if( xa == rec[3].x ) edge_pt[3].push_back({ya,aid}); 51 if( ya == rec[2].y ) edge_pt[2].push_back({xa,aid}); 52 if( ya == rec[4].y ) edge_pt[4].push_back({xa,aid}); 53 if( xb == rec[1].x ) edge_pt[1].push_back({yb,bid}); 54 if( xb == rec[3].x ) edge_pt[3].push_back({yb,bid}); 55 if( yb == rec[2].y ) edge_pt[2].push_back({xb,bid}); 56 if( yb == rec[4].y ) edge_pt[4].push_back({xb,bid}); 57 } 58 void build_G(){ 59 for(int i = 1;i<=n;++i){ 60 if( p[i].x == p[i+1].x){ 61 // cerr<<i<<" "<<p[i].x<<" "<<p[i].y<<p[i+1].x<<" "<<p[i+1].y<<rec[1].x<<" "<<rec[3].x<<" !!!"<<endl; 62 if( p[i].x <= rec[1].x || p[i].x >= rec[3].x ) continue; 63 bool up = (p[i].y < p[i+1].y); 64 int mx = max(p[i].y,p[i+1].y) , mi = min(p[i].y,p[i+1].y); 65 mx = min(mx,rec[1].y) ; mi = max(mi,rec[2].y); 66 if( mi < mx){ 67 // cerr<<i<<" "<<mi<<" "<<mx<<"miomx"<<endl; 68 if(up) add(p[i].x,mi,p[i].x,mx); 69 else add(p[i].x,mx,p[i].x,mi); 70 } 71 } 72 else{ 73 if( p[i].y >= rec[1].y || p[i].y <= rec[3].y ) continue; 74 bool ri = (p[i].x < p[i+1].x); 75 int mx = max(p[i].x,p[i+1].x) , mi = min(p[i].x,p[i+1].x); 76 mx = min(mx,rec[3].x) ; mi = max(mi,rec[1].x); 77 if( mi < mx){ 78 if(ri) add(mi,p[i].y,mx,p[i].y); 79 else add(mx,p[i].y,mi,p[i].y); 80 } 81 } 82 } 83 // cerr<<cnt<<" "<<cnt<<"!"<<endl; 84 if( cnt == 0 ){ 85 if(judge()) printf("1"); 86 else printf("0"); 87 exit(0); 88 } 89 for(int i = 1;i<=4;++i){ 90 if( mp[{rec[i].x,rec[i].y}]) continue; 91 mp[{rec[i].x,rec[i].y}] = ++cnt; 92 int x = rec[i].x , y = rec[i].y; 93 if( x == rec[1].x ) edge_pt[1].push_back({y,cnt}); 94 if( x == rec[3].x ) edge_pt[3].push_back({y,cnt}); 95 if( y == rec[2].y ) edge_pt[2].push_back({x,cnt}); 96 if( y == rec[4].y ) edge_pt[4].push_back({x,cnt}); 97 } 98 for(int i = 1;i<=4;++i){ 99 sort(edge_pt[i].begin(),edge_pt[i].end()); 100 if( i == 1 || i == 4) reverse(edge_pt[i].begin(),edge_pt[i].end()); 101 if( mp[{rec[i].x,rec[i].y}] == 0) mp[{rec[i].x,rec[i].y}] = ++cnt; 102 if( mp[{rec[i+1].x,rec[i+1].y}] == 0) mp[{rec[i+1].x,rec[i+1].y}] = ++cnt; 103 int s = mp[{rec[i].x,rec[i].y}] , e = mp[{rec[i+1].x,rec[i+1].y}]; 104 for(int j = 0;j<edge_pt[i].size();++j){ 105 if( j == edge_pt[i].size() - 1 && edge_pt[i][j].second != e) G[edge_pt[i][j].second].push_back(e); 106 if( j == 0 && edge_pt[i][j].second != s) G[s].push_back(edge_pt[i][j].second); 107 if( j!= edge_pt[i].size()-1) G[edge_pt[i][j].second].push_back(edge_pt[i][j+1].second); 108 } 109 } 110 } 111 bool dfs(int u,int fa){ 112 // cerr<<u<<" dfsu"<<endl; 113 for(auto v : G[u]){ 114 // cerr<<v<<" v"<<endl; 115 if(v == fa){ 116 // cerr<<v<<" findv"<<endl; 117 return 1; 118 } 119 if( vis[v] == 0){ 120 vis[v] = 1; 121 if( dfs(v,fa) ) return 1; 122 } 123 } 124 return 0; 125 } 126 int main(){ 127 int x1,y1,x2,y2; 128 scanf("%d %d %d %d",&x1,&y1,&x2,&y2); 129 rec[1] = (Point){x1,y1}; 130 rec[2] = (Point){x1,y2}; 131 rec[3] = (Point){x2,y2}; 132 rec[4] = (Point){x2,y1}; 133 rec[5] = rec[1]; 134 scanf("%d",&n); 135 for(int i = 1;i<=n;++i) scanf("%d %d",&p[i].x,&p[i].y); 136 p[n+1] = p[1]; 137 138 bool cont = 1; 139 for(int i = 1;i<=n;++i){ 140 if( p[i].x < rec[1].x || p[i].x > rec[3].x || p[i].y < rec[2].y || p[i].y > rec[4].y) cont = 0; 141 } 142 if( cont){ 143 printf("1"); 144 return 0; 145 } 146 build_G(); 147 // for(auto it : mp){ 148 // cerr<<it.first.first<<" "<<it.first.second<<" "<<it.second<<"!"<<endl; 149 // } 150 int ans = 0; 151 for(int i = 1;i<=cnt;++i){ 152 if(!vis[i]){ 153 vis[i] = 1; 154 if(dfs(i,i)) ++ans; 155 } 156 } 157 printf("%d",ans); 158 } 159 /* 160 0 5 10 0 161 8 162 0 0 163 2 0 164 2 8 165 8 8 166 8 0 167 10 0 168 10 10 169 0 10 170 */
5.7(补):首先具体说说做法:就是逆时针建图,包括矩形。然后dfs的意思是,找一个点,如果能dfs回自身,说明是一个合法区域,然后不会走过经过的点。然后今天早上的时候发现做法有点假,就是假如我先从矩形顶点出发,绕矩形一圈,那么剩下的那些合法的,靠着矩形边的区域就统计不到。再比如,假如我从一个合法区域开始dfs,假如我先走矩形边,走到另一个区域,那么我自身的区域就统计不到,而且我走到的另一个区域因为打了vis标记,所以也统计不到。
然后我想了一个下午怎么修改,但是还是没有解决。然后我突然发现,其实我原来的做法的一个小细节能够把这些繁琐的问题解决,就是我建点是先建立多边形上的,再建立矩形上的,边也是一样。所以我们在遍历起点的时候,会优先遍历多边形上的,第一个问题解决了。第二个问题??一样,我们dfs优先遍历多边形上的边,也就保证我先走自身的那个区域,然后我dfs到自身就直接return,所以链接两个区域的矩形边不会被遍历到。
今天虽然没做新题,但是巩固了昨天的题。从觉得自己昨天就是个傻逼,写出这垃圾算法,怀疑cf数据,想明白之后才发现昨天自己居然写了一个神仙算法自己都没发现,哈哈哈哈哈。
2020.5.8 第十七题:codeforces887E
这题其实挺简单的,就是对于每个圆,二分求出不合法区域,全部求出来之后跑一遍扫描线就好了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 1e12+9 4 #define eps 1e-7 5 struct Point{ 6 double x,y,r; 7 Point operator + (const Point& b)const{ 8 return (Point){x+b.x,y+b.y}; 9 } 10 Point operator - (const Point& b)const{ 11 return (Point){x-b.x,y-b.y}; 12 } 13 Point operator * (const double& b)const{ 14 return (Point){x*b,y*b}; 15 } 16 Point operator / (const double& b)const{ 17 return (Point){x/b,y/b}; 18 } 19 double len(){ return sqrt(x*x+y*y); } 20 }p0,p1,p2,v; 21 double cross(Point a,Point b,Point c){ 22 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 23 } 24 vector< pair<double,int> > vec; 25 void add(Point tem){ 26 double L = -inf , R = inf; 27 double a1,a2; 28 if(cross(p1,p2,tem) > eps ) swap(L,R); 29 for(int k = 1;k<=100;++k){ 30 // while(fabs(R-L) > eps){ 31 double m = (L+R)/2; 32 Point pm = p0 + v * m; 33 double len = (pm-p1).len(); 34 double dis = (pm-tem).len(); 35 // cerr<<L<<" "<<R<<" "<<m<<" "<<dis<<" "<<tem.r<<" "<<len<<" !!"<<endl; 36 // cerr<<pm.x<<" "<<pm.y<<" xy"<<endl; 37 if( fabs(dis - tem.r) < len ) L = m; 38 else R = m; 39 } 40 // cerr<<L<<" !"<<endl; 41 a1 = L; 42 43 L = -inf , R = inf; 44 if(cross(p1,p2,tem) > eps ) swap(L,R); 45 for(int k = 1;k<=100;++k){ 46 // while( fabs(R-L) > eps){ 47 double m = (L+R)/2; 48 Point pm = p0 + v * m; 49 double len = (pm-p1).len(); 50 double dis = (pm-tem).len(); 51 if( fabs(dis + tem.r) < len ) L = m; 52 else R = m; 53 } 54 // cerr<<L<<" !"<<endl; 55 a2 = R; 56 if( a1 > a2 ) swap(a1,a2); 57 // cerr<<a1<<" "<<a2<<" a12"<<endl; 58 vec.push_back({a1,1}); 59 vec.push_back({a2,-1}); 60 } 61 void solve(){ 62 vec.push_back({0,0}); 63 sort(vec.begin(),vec.end()); 64 double ans = inf; 65 int cnt = 0; 66 for(int i = 0;i<vec.size();++i){ 67 // cerr<<vec[i].first<<" "<<vec[i].second<<" "<<cnt<<" cnmt"<<endl; 68 if(cnt == 0 ) ans = min(ans,fabs(vec[i].first) ); 69 cnt += vec[i].second; 70 if(cnt == 0 ) ans = min(ans,fabs(vec[i].first) ); 71 } 72 // cerr<<ans<<" !"<<endl; 73 double len = (p2-p1).len() * 0.5; 74 ans = sqrt(ans*ans + len*len); 75 printf("%.10f",ans); 76 return; 77 } 78 int main(){ 79 scanf("%lf %lf %lf %lf",&p1.x,&p1.y,&p2.x,&p2.y); 80 p0 = (p1+p2)*0.5; 81 v.x = -(p2-p1).y; v.y = (p2-p1).x; 82 v = v / v.len(); 83 int n; scanf("%d",&n); 84 for(int i = 1;i<=n;++i){ 85 Point tem; scanf("%lf %lf %lf",&tem.x,&tem.y,&tem.r); 86 add(tem); 87 } 88 solve(); 89 } 90 /* 91 0 0 10 0 92 1 93 5 2 1 94 */
2020.5.10 第十八题:codeforces575E
给你若干点,在这些点中选三个点,使得外接圆包含所有点,同时满足半径最大。
先求凸包,外接圆最大,肯定三个点接近比较好,所以枚举凸包连续的三个点即可。然后因为最小原覆盖的定理说明肯定能选择三个点满足外接圆包含所有点,所以肯定找得到。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll lim = 1e5; 5 const int N = 8e5+9; 6 struct Point{ 7 ll x,y; 8 bool operator < (const Point& b)const{ 9 if( x == b.x ) return y < b.y; 10 return x < b.x; 11 } 12 }p[N],ch[N],ans[5]; 13 int cnt = 0; 14 ll cross(Point a,Point b,Point c){ 15 return (b.x-a.x) * (c.y - a.y) - (c.x - a.x) * (b.y-a.y); 16 } 17 double dist(Point a,Point b){ 18 return sqrt( 1.0*(a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y-b.y) ); 19 } 20 void add(Point tem,ll v){ 21 if(tem.x - v >= 0 ) p[cnt++] = (Point){tem.x - v,tem.y}; 22 else{ 23 ll deta = v - tem.x; 24 ll ya = max((ll)0,tem.y-deta); 25 ll yb = min(lim,tem.y + deta); 26 p[cnt++] = (Point){(ll)0,ya}; 27 p[cnt++] = (Point){(ll)0,yb}; 28 } 29 30 if(tem.x + v <= lim ) p[cnt++] = (Point){tem.x + v,tem.y}; 31 else{ 32 ll deta = v + tem.x - lim; 33 ll ya = max((ll)0,tem.y-deta); 34 ll yb = min(lim,tem.y + deta); 35 p[cnt++] = (Point){lim,ya}; 36 p[cnt++] = (Point){lim,yb}; 37 } 38 39 if(tem.y - v >= 0 ) p[cnt++] = (Point){tem.x,tem.y-v}; 40 else{ 41 ll deta = v - tem.y; 42 ll xa = max((ll)0,tem.x-deta); 43 ll xb = min(lim,tem.x + deta); 44 p[cnt++] = (Point){xa,(ll)0}; 45 p[cnt++] = (Point){xb,(ll)0}; 46 } 47 48 if(tem.y + v <= lim ) p[cnt++] = (Point){tem.x,tem.y+v}; 49 else{ 50 ll deta = v + tem.y - lim; 51 ll xa = max((ll)0,tem.x-deta); 52 ll xb = min(lim,tem.x + deta); 53 p[cnt++] = (Point){xa,lim}; 54 p[cnt++] = (Point){xb,lim}; 55 } 56 } 57 int Andrew(){ 58 sort(p,p+cnt); 59 int m = 0; 60 for(int i = 0;i<cnt;++i){ 61 while( m > 1 && cross( ch[m-2] , p[i], ch[m-1]) >= 0 ) --m; 62 ch[m++] = p[i]; 63 } 64 int k = m; 65 for(int i = cnt-2;i>=0;--i){ 66 while( m > k && cross(ch[m-2] , p[i],ch[m-1]) >= 0 ) --m; 67 ch[m++] = p[i]; 68 } 69 if(cnt > 1) -- m; 70 return m; 71 } 72 double CirR(Point a,Point b,Point c){ 73 double sth = 1.0 * abs( cross(a,b,c) )/( dist(a,b) * dist(a,c) ) ; 74 return dist(b,c) / sth; 75 } 76 int main(){ 77 int n; scanf("%d",&n); 78 for(int i = 1;i<=n;++i){ 79 ll v; 80 Point tem; scanf("%lld %lld %lld",&tem.x,&tem.y,&v); 81 add(tem,v); 82 } 83 int m = Andrew(); 84 double res = 0; 85 ch[m] = ch[0]; ch[m+1] = ch[1]; 86 for(int i = 0;i<m;++i){ 87 if( CirR(ch[i],ch[i+1],ch[i+2]) > res){ 88 res = CirR(ch[i],ch[i+1],ch[i+2]); 89 ans[0] = ch[i]; 90 ans[1] = ch[i+1]; 91 ans[2] = ch[i+2]; 92 } 93 } 94 for(int i = 0;i<3;++i) printf("%lld %lld\n",ans[i].x,ans[i].y); 95 return 0; 96 }
2020.5.11 第十九题:codeforces717I Cowboy Beblop at his computer
题意:三维空间给你两个平面环,问两个平面环是否“well-connect"(定义:不能平移,缩小旋转使得两个环分开),其实就是两个橡皮筋是否套起来。
做法: 这是我第四道三维计几。因为一个小细节wa了一发。思路挺好想的:题意已经告诉我们,只需要统计b环的线段中,从不同方向穿过a环的数量,比较数量是否相同即可。然后我们要做的就是把交点全求出来,然后两平面相交是直线,所以点都在一个直线上,然后我们求出直线和a环的交点,用排个序,用射线法就可以处理出点是否在环a内。然后统计就好了。
细节(wa点):b环线段满足什么条件才统计进入答案?统计起点不统计终点是不行的,用射线法常用方法就是统计高出线段端点,不统计线段低点即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+9; 4 #define eps 1e-8 5 struct Point{ 6 double x,y,z; 7 int ty; 8 Point operator - (const Point& b)const{ 9 return (Point){x-b.x,y-b.y,z-b.z}; 10 } 11 Point operator + (const Point& b)const{ 12 return (Point){x+b.x,y+b.y,z+b.z}; 13 } 14 double len(){ 15 return sqrt(x*x+y*y+z*z); 16 } 17 Point operator * (const double& b)const{ 18 return (Point){x*b,y*b,z*b}; 19 } 20 }pn[N],pm[N],hn,hm,Linep[N*2]; 21 pair<double,int> num[N*2]; 22 int n,m,cnt; 23 double dot(Point a,Point b){ 24 return a.x*b.x + a.y * b.y + a.z*b.z; 25 } 26 Point cross(Point a,Point b){ 27 return (Point){a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x}; 28 } 29 //直线和平面交点 30 Point LinePlaneIntersection(Point p1,Point p2,Point p0,Point n){ 31 Point v = p2 - p1; 32 double t = (dot(n,p0-p1) / dot(n,p2-p1)); 33 return p1 + v*t; 34 } 35 Point LineCross(Point a,Point b,Point c,Point d){ 36 Point n = cross(b-a,d-c); 37 n = d+n; 38 Point f = cross(d-c,n-c); 39 double t = dot(f,c-a)/dot(f,b-a); 40 Point res = a + (b-a)*t; 41 return res; 42 } 43 void solve(){ 44 cnt = 0; 45 for(int i = 1;i<=n;++i){ 46 double nf = dot(hm,pn[i] - pm[1]); 47 double nt = dot(hm,pn[i+1] - pm[1]); 48 bool cro = 0; 49 if( nf * nt < -eps ) cro = 1; 50 else if( nf * nt < eps ){ 51 if(nf < -eps || nt < -eps ) cro = 1; 52 } 53 if( cro ){ 54 int tp; 55 if(nf < -eps ) tp = 1; 56 else tp = -1; 57 Linep[++cnt] = LinePlaneIntersection(pn[i],pn[i+1],pm[1],hm); 58 Linep[cnt].ty = tp; 59 } 60 } 61 // cerr<<cnt<<"!"<<endl; 62 if(cnt == 0 ){ 63 puts("NO"); 64 return; 65 } 66 Point lv = cross(hn,hm); 67 for(int i = 1;i<=m;++i){ 68 Point mf = cross(lv,pm[i]-Linep[1]); 69 Point mt = cross(lv,pm[i+1]-Linep[1]); 70 bool cro = 0; 71 if( dot(mf,mt) < -eps ) cro = 1; 72 else if(dot(mf,mt) < eps){ 73 if(dot(hm,mf) > eps || dot(hm,mt) > eps ) cro = 1; 74 } 75 if( cro ){ 76 Linep[++cnt] = LineCross(Linep[1],Linep[1]+lv,pm[i],pm[i+1]); 77 Linep[cnt].ty = 0; 78 } 79 } 80 // cerr<<cnt<<endl; 81 for(int i = 1;i<=cnt;++i){ 82 num[i].first = dot(Linep[i] - Linep[1],lv); 83 num[i].second = Linep[i].ty; 84 } 85 sort(num+1,num+1+cnt); 86 int a0 = 0 , a1 = 0; 87 int now = 0; 88 for(int i = cnt;i>=1;--i){ 89 if(num[i].second == 0 ) ++now; 90 else if(now&1){ 91 if(num[i].second == 1) ++a1; 92 else ++a0; 93 } 94 } 95 if(a1 == a0 ) puts("NO"); 96 else puts("YES"); 97 } 98 int main(){ 99 scanf("%d",&n); 100 for(int i = 1;i<=n;++i){ 101 int xx,yy,zz; scanf("%d %d %d",&xx,&yy,&zz); 102 pn[i].x = xx; pn[i].y = yy; pn[i].z = zz; 103 } 104 scanf("%d",&m); 105 for(int i = 1;i<=m;++i){ 106 int xx,yy,zz; scanf("%d %d %d",&xx,&yy,&zz); 107 pm[i].x = xx; pm[i].y = yy; pm[i].z = zz; 108 } 109 pn[n+1] = pn[1]; pm[m+1] = pm[1]; 110 hn = cross(pn[2]-pn[1],pn[3]-pn[1]); 111 hm = cross(pm[2]-pm[1],pm[3]-pm[1]); 112 Point tem = cross(hn,hm); 113 if(tem.len() < eps){ 114 puts("NO"); 115 return 0; 116 } 117 solve(); 118 }
2020.5.17第二十题:http://codeforces.com/group/uVAsoW2Jkj/contest/280056/attachments
这是我们的训练赛的F题。最后我没过。。。主要是对最小圆覆盖没理解透彻以及一开始想歪思路了。
题意:给你300个黑白点,求出最小半径圆包含全部黑点而不包含白点。
一开始看到300,打算直接在黑点凸包上暴力枚举三个点。但其实这个做法很假,比如两个黑点连线,一个白点很靠近连线,那么我肯定不能够只考虑这两个黑点画圆,也就是我卡这个圆不仅仅要考虑黑点,白点也要卡。那么就是最小圆覆盖了。最小圆覆盖的本质就是卡圆的边界,枚举圆上的三个点,从而来卡这个圆。可以注意到,黑点不在当前圆,那么黑点肯定在答案圆弧上,白点如果在当前圆内,那么这个白点肯定也在圆弧上,然后就是板子了。注意的是圆弧上有黑白点,如果白色点的只有一段,那么圆是合法的,因为可以向一个方向轻微挪动,多于一段则不可。
哎。。。这几天没有练计几,就没有做出计几题了。然后一个学弟做出来了。难受。。。一开始光想着怎么暴力去了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 300+9; 4 const double inf = 1e20; 5 #define eps 1e-8 6 struct Point{ 7 double x,y; 8 int ty; 9 bool operator < (const Point& b)const{ 10 return x < b.x || (x==b.x && y < b.y); 11 } 12 Point operator - (const Point& b)const{ 13 return (Point){x-b.x,y-b.y}; 14 } 15 Point rotate(){ 16 return (Point){-y,x}; 17 } 18 int len(){ 19 return sqrt(x*x+y*y); 20 } 21 }p[N],p0; 22 int n,na,nb; 23 struct Circle{ 24 Point o; 25 double r; 26 }; 27 int sgn(double x){ 28 if(fabs(x) < eps) return 0; 29 if(x<0) return -1; 30 return 1; 31 } 32 Point getmid(Point a,Point b){ 33 return (Point){(a.x+b.x)*0.5,(a.y+b.y)*0.5}; 34 } 35 double cross(Point a,Point b,Point c){ 36 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 37 } 38 double dis(Point a,Point b){ 39 return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) ); 40 } 41 int Qua(Point a){ 42 if(a.x >= 0 && a.y >= 0 ) return 1; 43 if( a.x <= 0 && a.y >= 0 ) return 2; 44 if(a.x <= 0 && a.y <= 0 ) return 3; 45 return 4; 46 } 47 bool cmp(Point a,Point b){ 48 Point ta = a - p0; 49 Point tb = b - p0; 50 if( Qua(ta) == Qua(tb) ){ 51 if(cross(p0,a,b) == 0 ) return a.len() < b.len(); 52 return cross(p0,a,b) > 0; 53 } 54 else return Qua(ta) < Qua(tb); 55 } 56 Point circlein(Point a,Point b,Point c){ 57 Point v1 = b-a , v2 = a-c; 58 v1 = v1.rotate(); v2=v2.rotate(); 59 double p1x = (a.x+b.x)*0.5 , p1y = (a.y+b.y)*0.5; 60 double p2x = (a.x+c.x)*0.5 , p2y = (a.y+c.y)*0.5; 61 double ux = p1x - p2x , uy = p1y - p2y; 62 double t = (v2.x * uy - ux*v2.y)/( 1.0*v1.x*v2.y - v2.x *v1.y); 63 double ox = p1x + v1.x*t; 64 double oy = p1y + v1.y*t; 65 Point res = (Point){ox,oy}; 66 return res; 67 } 68 bool judge(Point a,Circle ans){ 69 if(a.ty){ 70 return dis(a,ans.o) > ans.r + eps; 71 } 72 return dis(a,ans.o) < ans.r - eps; 73 } 74 void solve(){ 75 if(na==0){ 76 puts("0"); 77 return; 78 } 79 random_shuffle(p+1,p+1+n); 80 Circle ans = (Circle){p[1],0}; 81 for(int i = 2;i<=n;++i){ 82 if(judge(p[i],ans)){ 83 ans = (Circle){p[i],0}; 84 for(int j = 1;j<i;++j){ 85 if(judge(p[j],ans)){ 86 ans.o = getmid(p[i],p[j]); 87 ans.r = dis(p[i],p[j])/2; 88 for(int k = 1;k<j;++k){ 89 if(judge(p[k],ans)){ 90 ans.o = circlein(p[i],p[j],p[k]); 91 ans.r = dis(p[i],ans.o); 92 cerr<<ans.o.x<<" "<<ans.o.y<<" "<<p[i].x<<" "<<p[i].y<<endl; 93 cerr<<ans.r<<" !!!"<<endl; 94 } 95 } 96 } 97 } 98 } 99 } 100 if(isinf(ans.r) || isnan(ans.r)){ 101 puts("The Orcs are close"); 102 return; 103 } 104 vector<Point> vec; 105 for(int i = 1;i<=n;++i){ 106 if(judge(p[i],ans)){ 107 puts("The Orcs are close"); 108 return; 109 } 110 if( sgn(dis(ans.o,p[i]) - ans.r) == 0 ) vec.push_back(p[i]); 111 } 112 p0 = ans.o; 113 sort(vec.begin(),vec.end(),cmp); 114 int has = 0; 115 for(int i = 0;i<vec.size();){ 116 if(vec[i].ty == 1 ){ 117 ++i; 118 continue; 119 } 120 ++has; 121 int j = i; 122 while(j+1<vec.size() && vec[j+1].ty == 0 )++j; 123 i = j+1; 124 } 125 bool ok = 0; 126 if(has<=1){ 127 ok = 1; 128 } 129 else{ 130 if(has>2) ok = 0; 131 else{ 132 if(vec[0].ty == 0 && vec[vec.size()-1].ty == 0 ) ok = 1; 133 else ok = 0; 134 } 135 } 136 if(ok) printf("%.15f\n",ans.r); 137 else puts("The Orcs are close"); 138 } 139 #define gc getchar() 140 inline int read(int &x){ 141 char ch; while(!isdigit(ch = gc)) if(ch == '\n' || ch == EOF) return 0; x = ch ^ 48; 142 while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48); return 1; 143 } 144 int main(){ 145 char ch; 146 while((ch = gc) != EOF){ 147 n = na = nb = 0; 148 int tx,ty; 149 while(read(tx)){ 150 ++n; 151 p[n].x = tx; 152 read(ty); 153 p[n].y = ty; 154 p[n].ty = 1; 155 ++na; 156 } 157 while(read(tx)){ 158 ++n; 159 ++nb; 160 p[n].x = tx; 161 read(ty); 162 p[n].y = ty; 163 p[n].ty = 0; 164 } 165 solve(); 166 } 167 }
(补):下面cf549题的启发,其实这题有个更简洁好懂的做法。就是黑点的凸包肯定有至少两个点,所以最终答案肯定是在凸包的两个点或三个点上面。然后我们枚举凸包上两个点,那么最终圆心会在这中垂线上,然后我们对于其余的黑点和白点,在这个中垂线上卡线段,最终卡出来的线段即更新答案。对于那种轻微偏移的情况,其实就是卡线段的开闭区间,不用判断白点是否一段。
这样看来这题真的就是傻逼题。当时以为共线就直接圆心终点,如果早点hack这个做法,然后顺着暴力思路往下想,就会想到枚举两点,第三点卡线段。啊,太菜了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N = 300+9; 4 const double inf = 1e18; 5 #define eps 1e-8 6 struct Point{ 7 double x,y; 8 int ty; 9 bool operator < (const Point& b)const{ 10 return x < b.x || (x==b.x && y < b.y); 11 } 12 Point operator - (const Point& b)const{ 13 return (Point){x-b.x,y-b.y}; 14 } 15 Point operator + (const Point& b)const{ 16 return (Point){x+b.x,y+b.y}; 17 } 18 Point operator * (const double& b)const{ 19 return (Point){x*b,y*b}; 20 } 21 }pa[N],pb[N],ch[N*2]; 22 int na,nb,m; 23 struct Circle{ 24 Point o; 25 double r; 26 }; 27 int sgn(double x){ 28 if(fabs(x) < eps) return 0; 29 if(x<0) return -1; 30 return 1; 31 } 32 double cross(Point a,Point b){return a.x*b.y-b.x*a.y;} 33 double dot(Point a,Point b){return a.x*b.x + a.y*b.y;} 34 double len2(Point a){ return a.x*a.x + a.y*a.y;} 35 double len(Point a){return sqrt(len2(a));} 36 Point core(Point a,Point b,Point c){ 37 Point A=(Point){b.x-a.x,c.x-a.x}*2 , B = (Point){b.y-a.y,c.y-a.y}*2; 38 Point C = (Point){ len2(b)-len2(a),len2(c)-len2(a) }; 39 return (Point){ cross(B,C)/cross(B,A),cross(A,C)/cross(A,B) }; 40 } 41 int Andrew(){ 42 sort(pa,pa+na); 43 int m = 0; 44 for(int i = 0;i<na;++i){ 45 while( m > 1 && cross(pa[i] - ch[m-2],ch[m-1] - ch[m-2])>=0) --m; 46 ch[m++] = pa[i]; 47 } 48 int k = m; 49 for(int i = na-2;i>=0;--i){ 50 while(m>k && cross(pa[i] - ch[m-2] ,ch[m-1] - ch[m-2])>=0) --m; 51 ch[m++] = pa[i]; 52 } 53 if(na>1) --m; 54 return m; 55 } 56 double work(int f,int t){ 57 double l = -inf , r = inf; 58 Point o = (ch[f] + ch[t])*0.5; 59 for(int i = f+1;i<t;++i){ 60 Point c = core(ch[f],ch[t],ch[i]); 61 double tem = len(c-o); 62 if( sgn(cross(ch[t] - ch[f] , c - ch[f])) > 0 ) tem = -tem; 63 if( sgn(tem - l) > 0 ) l = tem; 64 } 65 for(int i = t+1;i<f+m;++i){ 66 Point c = core(ch[f],ch[t],ch[i]); 67 double tem = len(c-o); 68 if( sgn(cross(ch[t] - ch[f] , c - ch[f])) > 0 ) tem = -tem; 69 if( sgn(r - tem) > 0 ) r = tem; 70 } 71 if(r < l - eps) return inf; 72 for(int i = 0;i<nb;++i){ 73 int fx = sgn(cross(ch[t] - ch[f],pb[i] - ch[f])); 74 if(fx == 0){ 75 if( sgn( dot(ch[f] - pb[i] , ch[t] - pb[i]) ) < 0 ) return inf; 76 } 77 else{ 78 Point c = core(ch[f],ch[t],pb[i]); 79 double tem = len(c-o); 80 if( sgn(cross(ch[t] - ch[f] , c - ch[f])) > 0 ) tem = -tem; 81 if( fx > 0 ){ 82 tem += 2*eps; 83 if( sgn(tem - l) > 0 ) l = tem; 84 } 85 else{ 86 tem -= 2*eps; 87 if( sgn(r - tem) > 0 ) r = tem; 88 } 89 } 90 } 91 if( r < l - eps ) return inf; 92 double tem = len(ch[f]-ch[t])*0.5; 93 if( r >-eps && l < eps) return tem; 94 double mi = min(fabs(r),fabs(l)); 95 tem = sqrt(tem*tem + mi*mi); 96 return tem; 97 } 98 void solve(){ 99 if(na <= 1){ 100 puts("0"); 101 return; 102 } 103 m = Andrew(); 104 for(int i = 0;i<m;++i) ch[i+m] = ch[i]; 105 double ans = inf; 106 for(int i = 0;i<m;++i){ 107 for(int j = i+1;j<m;++j){ 108 ans = min(ans,work(i,j)); 109 } 110 } 111 if( ans == inf ) puts("The Orcs are close"); 112 else printf("%.12f\n",ans); 113 } 114 #define gc getchar() 115 inline int read(int &x){ 116 char ch; while(!isdigit(ch = gc)) if(ch == '\n' || ch == EOF) return 0; x = ch ^ 48; 117 while(isdigit(ch = gc)) x = x * 10 + (ch ^ 48); return 1; 118 } 119 int main(){ 120 char ch; 121 while((ch = gc) != EOF){ 122 int tx,ty; 123 na = nb = 0; 124 while(read(tx)){ 125 pa[na].x = tx; 126 read(ty); 127 pa[na].y = ty; 128 ++na; 129 } 130 while(read(tx)){ 131 pb[nb].x = tx; 132 read(ty); 133 pb[nb].y = ty; 134 ++nb; 135 } 136 solve(); 137 } 138 }
2020.5.19 第二十一题:codeforces549E
题意:给你10000黑白点,问是否有圆可以把黑白点严格分割开。
题解:这题用上一题的最小圆覆盖做法居然没过????震惊。。。
这题做法:https://blog.csdn.net/zava_1087/article/details/46447069
就是二维划为三维,然后卡三维的棱变成卡二维的棱。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-10 4 #define inf 1e16 5 const int N = 1e4+9; 6 struct Point{ 7 double x,y; 8 Point operator - (const Point& b)const{ 9 return (Point){x-b.x,y-b.y}; 10 } 11 Point operator + (const Point& b)const{ 12 return (Point){x+b.x,y+b.y}; 13 } 14 Point operator* (const double& b)const{ 15 return (Point){x*b,y*b}; 16 } 17 bool operator < (const Point& b)const{ 18 return x < b.x || ( x==b.x && y < b.y) ; 19 } 20 }pa[N],pb[N],ha[N],hb[N]; 21 int na,nb,nha,nhb; 22 vector< pair<int,int> > edge; 23 int sgn(double x){ 24 if(fabs(x) < eps) return 0; 25 if(x<0) return -1; 26 return 1; 27 } 28 double cross(Point a,Point b){return a.x*b.y-b.x*a.y;} 29 double dot(Point a,Point b){return a.x*b.x + a.y*b.y;} 30 double len2(Point a){ return a.x*a.x + a.y*a.y;} 31 double len(Point a){return sqrt(len2(a));} 32 Point core(Point a,Point b,Point c){ 33 Point A=(Point){b.x-a.x,c.x-a.x}*2 , B = (Point){b.y-a.y,c.y-a.y}*2; 34 Point C = (Point){ len2(b)-len2(a),len2(c)-len2(a) }; 35 return (Point){ cross(B,C)/cross(B,A),cross(A,C)/cross(A,B) }; 36 } 37 void Andrew(Point p [],int n,Point ch[],int& m){ 38 sort(p,p+n); 39 m = 0; 40 for(int i = 0;i<n;++i){ 41 while( m > 1 && cross(p[i] - ch[m-2],ch[m-1] - ch[m-2])>=0) --m; 42 ch[m++] = p[i]; 43 } 44 int k = m; 45 for(int i = n-2;i>=0;--i){ 46 while(m>k && cross(p[i] - ch[m-2] ,ch[m-1] - ch[m-2])>=0) --m; 47 ch[m++] = p[i]; 48 } 49 if(n>1) --m; 50 } 51 bool check(Point A[],int na,Point B[],int nb,int l,int r){ 52 if(na==1){ 53 return 1; 54 } 55 Point o = (A[l]+A[r])*0.5; 56 double mi = -inf, mx = inf; 57 int mid = -1; 58 bool res = 1; 59 for(int i = l+1;i<r;++i){ 60 Point c = core(A[l],A[r],A[i]); 61 double d = len(c - o); 62 if( sgn(cross(c-A[l],A[r]-A[l])) < 0 ) d = -d; 63 if( mi < d ){ 64 mi = d; 65 mid = i; 66 } 67 if( mx <= mi ) res = 0; 68 } 69 for(int i = (r+1)%na ;i!=l;i = (i+1)%na ){ 70 Point c = core(A[l],A[r],A[i]); 71 double d = len(c - o); 72 if( sgn(cross(c-A[l],A[r]-A[l])) < 0 ) d = -d; 73 if( mx > d ) mx = d; 74 if( mx <= mi) res = 0; 75 } 76 if( mx < mi + eps) res = 0; 77 for(int i = 0;i<nb && res;++i){ 78 int fx = sgn(cross(B[i]-A[l],A[r]-A[l])); 79 if(fx == 0){ 80 if(sgn(dot(A[r]-B[i],A[l]-B[i])) < 0 ) res = 0; 81 } 82 else{ 83 Point c = core(A[l],A[r],B[i]); 84 double d = len(c-o); 85 if( sgn(cross(c-A[l],A[r]-A[l])) < 0 ) d = -d; 86 if(fx > 0 ){ 87 if(mx > d ) mx = d; 88 } 89 else{ 90 if( mi < d ) mi = d; 91 } 92 } 93 if( mx <= mi) res = 0; 94 } 95 return res || (mid!=-1 &&(check(A,na,B,nb,l,mid) || check(A,na,B,nb,mid,r))); 96 } 97 int main(){ 98 scanf("%d %d",&na,&nb); 99 for(int i = 0;i<na;++i) scanf("%lf %lf",&pa[i].x,&pa[i].y); 100 for(int i = 0;i<nb;++i) scanf("%lf %lf",&pb[i].x,&pb[i].y); 101 Andrew(pa,na,ha,nha); 102 Andrew(pb,nb,hb,nhb); 103 if( check(ha,nha,pb,nb,0,nha-1) || check(hb,nhb,pa,na,0,nhb-1) ) puts("YES"); 104 else puts("NO"); 105 return 0; 106 }
2020.5.23:第二十二题:集训队训练,就一个扫描线,不写题解。
2020.5.29 : 第二十三题:codeforces1359F
详见:https://i.cnblogs.com/posts/edit-done;postId=12989198
2020.6.1 第二十四题:集训队训练,就是计几+状压dp,不写了
2020.6.7 第二十五提:集训队训练:http://codeforces.com/group/uVAsoW2Jkj/contest/283255/problem/F
没想到咕咕咕了那么多天,因为期末了,然后很多事要做,实训实验什么的。
题意:二维平面给了你15个矩形,坐标1000以内,然后问你一条长度为L的线段最多能和多少矩形接触。
题解:首先很容易发现答案线段的一个端点一定是矩形一条边上的一个点(s点),并且某个矩形的一个端点(t点)一定在答案线段上。然后想着证明别的更好的结论,但是太菜了证明半天没证明出来,但是我们发现这个结论也足够我们ac了。因为坐标1000,所以我们可以暴力枚举矩形端点以及边上整点来统计。但是有个很重要的问题就是,有可能答案线段的s点不是整点,比如黄色线段,那么如果我们枚举s点上下的整点,会出现一种情况就是刚好统计漏了答案,也就是红蓝线段所示。
但是有一个极其重要的证明就是,即使是上述情况,我们也可以通过枚举整点来统计出答案,因为从红色线段到蓝色线段中间肯定经过绿色线段(统计漏的答案矩形的端点),也就是绿色线段,所以我们还是能通过上述方法统计答案,最后就是大暴力了。复杂度5e7,但是可以剪枝优化,所以远远达不到(甚至还没优化就擦线过了。。)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long double dd; 4 const dd eps = 1e-9; 5 int sgn(dd x){ 6 if(fabs(x) < eps ) return 0; 7 if(x<0) return -1; 8 return 1; 9 } 10 struct Point{ 11 dd x,y; 12 Point operator + (const Point& b)const{ 13 return (Point){x+b.x,y+b.y}; 14 } 15 Point operator - (const Point& b)const{ 16 return (Point){x-b.x,y-b.y}; 17 } 18 Point operator / (const double& b)const{ 19 return (Point){x/b,y/b}; 20 } 21 Point operator * (const double& b)const{ 22 return (Point){x*b,y*b}; 23 } 24 dd len(){return sqrt(x*x+y*y); } 25 }p[5][16]; 26 dd cross(Point a,Point b,Point c){ 27 return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y); 28 } 29 dd L; 30 int n; 31 32 //两线段是否相交 33 bool insert(Point s1,Point t1,Point s2,Point t2,bool show = 0 ){ 34 // if(show){ 35 // // cerr<<s1.x<<" "<<s1.y<<" s1"<<endl; 36 // // cerr<<t1.x<<" "<<t1.y<<" t1"<<endl; 37 // // cerr<<s2.x<<" "<<s2.y<<" s2"<<endl; 38 // // cerr<<t2.x<<" "<<t2.y<<" t2"<<endl; 39 // // cerr<< cross(s1,s2,t1) <<" sgn1"<<endl; 40 // // cerr<< cross(s1,t2,t1) <<" sgn2"<<endl; 41 // // cerr<< cross(s2,s1,t2) <<" sgn3"<<endl; 42 // // cerr<< cross(s2,t1,t2) <<" sgn"<<endl; 43 // } 44 return 45 max(s1.x,t1.x) >= min(s2.x,t2.x) && 46 max(s2.x,t2.x) >= min(s1.x,t1.x) && 47 max(s1.y,t1.y) >= min(s2.y,t2.y) && 48 max(s2.y,t2.y) >= min(s1.y,t1.y) && 49 sgn( cross(s1,s2,t1) ) * sgn( cross(s1,t2,t1) )<=0 && 50 sgn( cross(s2,s1,t2) ) * sgn( cross(s2,t1,t2) )<=0; 51 } 52 int solve(Point a,Point b){ 53 // Point test = a; 54 Point s = b; 55 Point deta = a - b; 56 deta = deta*L/deta.len(); 57 a = b + deta; 58 int res = 0; 59 for(int i = 1;i<=n;++i){ 60 bool ok = 0; 61 for(int k = 0;k<=3 && (!ok) ;++k){ 62 if( insert(b,a,p[k][i],p[k+1][i])) ok = 1; 63 } 64 if(ok) ++res; 65 } 66 return res; 67 } 68 int main(){ 69 int tL; 70 scanf("%d %d",&n,&tL); 71 L = tL; 72 for(int i = 1;i<=n;++i){ 73 int xa,xb,ya,yb; 74 scanf("%d %d %d %d",&xa,&ya,&xb,&yb); 75 p[0][i].x = xa; p[0][i].y = ya; 76 p[1][i].x = xb; p[1][i].y = ya; 77 p[2][i].x = xb; p[2][i].y = yb; 78 p[3][i].x = xa; p[3][i].y = yb; 79 p[4][i] = p[0][i]; 80 } 81 int ans = 1; 82 for(int i = 1;i<=n;++i){ 83 for(int k = 0;k<4;++k){ 84 for(int j = 1;j<=n;++j){ 85 for(int x = p[0][j].x;x<=p[1][j].x;++x){ 86 Point tem = (Point){1.0*x,p[0][j].y}; 87 ans = max(ans,solve(p[k][i],tem)); 88 tem.y = p[3][j].y; 89 ans = max(ans,solve(p[k][i],tem)); 90 } 91 for(int y = p[0][j].y;y<=p[3][j].y;++y){ 92 Point tem = (Point){p[0][j].x,1.0*y}; 93 ans = max(ans,solve(p[k][i],tem)); 94 tem.x = p[1][j].x; 95 ans = max(ans,solve(p[k][i],tem)); 96 } 97 } 98 } 99 } 100 printf("%d",ans); 101 }
由于搞项目,然后最近我们要选方向,我选了移动开发,所以又抽了点时间学安卓,然后又为了恢复就vp了几天,计几现在才做回来。
之前做了一道统计包含原点三角形的题,其实转换成不包含原点三角形就好了,就不贴了(25题)
2020.7.3第二十六题:codeforces886F
题意:给你2000个点,问你有多少条过原点的直线满足,所有点在该直线的投影点,是中心对称的。
看了看题解,知道做法就是:首先结论,如果投影中心对称,那么在该直线上这个中心对称点肯定是投影点的重心,那么反投影回去,也就是我对原来的点求个重心,那么最后投影中心肯定是重心的投影(写写柿子,用一个向量表示直线,然后投影用点积表示就知道了)
然后我们先把关于重心对称的点对删去,在剩下的点中枚举第一个点和谁对称,然后确定这条直线,然后扫一遍点判断直线符不符合即可。
这题由于担心被卡精度,所以全程用longlong,果然一切顺利哈哈哈
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N = 2e3+9; 5 bool used[N]; 6 struct Point{ 7 ll x,y; 8 Point operator + (const Point& b)const{ 9 return (Point){x+b.x,y+b.y}; 10 } 11 Point operator / (const ll& b)const{ 12 return (Point){x/b,y/b}; 13 } 14 Point operator * (const ll& b)const{ 15 return (Point){x*b,y*b}; 16 } 17 Point rotate(){ 18 return (Point){-y,x}; 19 } 20 bool operator < (const Point& b)const{ 21 if(x==b.x) return y < b.y; 22 return x < b.x; 23 } 24 bool operator == (const Point& b)const{ 25 return x==b.x && y == b.y; 26 } 27 ll dot(const Point& b)const{ 28 return x*b.x + y*b.y; 29 } 30 }pt[N],p0; 31 int n; 32 vector<Point> p; 33 map<ll,int> mp; 34 map<double,int> line_vis; 35 void pre_solve(){ 36 for(int i = 1;i<=n;++i){ 37 if(used[i]) continue; 38 for(int j = i+1;j<=n;++j){ 39 if(used[j]) continue; 40 Point tp = (pt[i] + pt[j]); 41 if( tp == p0 * 2){ 42 used[i] = used[j] = 1; 43 } 44 } 45 } 46 for(int i = 1;i<=n;++i){ 47 if(used[i]) continue; 48 p.push_back(pt[i]); 49 } 50 } 51 bool judge(Point line){ 52 mp.clear(); 53 ll p0_d = p0.dot(line); 54 for(auto v : p){ 55 ll d = v.dot(line); 56 d -= p0_d; 57 mp[ d ]++; 58 } 59 for(auto it : mp){ 60 ll val = it.first; 61 int num = it.second; 62 if(val == 0 ) continue; 63 if(mp[-val] != num) return 0; 64 } 65 return 1; 66 } 67 int main(){ 68 scanf("%d",&n); 69 for(int i = 1;i<=n;++i){ 70 scanf("%lld %lld",&pt[i].x,&pt[i].y); 71 pt[i] = pt[i] * n; 72 p0 = p0 + pt[i]; 73 } 74 if(n<=2){ 75 puts("-1"); 76 return 0; 77 } 78 p0 = p0 / n; 79 pre_solve(); 80 if( p.size() == 0 ){ 81 puts("-1"); 82 return 0; 83 } 84 int ans= 0; 85 for(int i = 0;i<p.size();++i){ 86 double tx = (p[0].x + p[i].x)*0.5; 87 double ty = (p[0].y + p[i].y)*0.5; 88 tx = p0.x - tx; ty = p0.y - ty; 89 ll temx = 2*tx , temy = 2*ty; 90 if(temy < 0 || (temy==0 && temx < 0)){ 91 temx = -temx; 92 temy = -temy; 93 } 94 Point vec = (Point){temx,temy}; 95 96 double the = atan2(temy,temx); 97 if(line_vis[the]) continue; 98 line_vis[the] = 1; 99 vec = vec.rotate(); 100 if(judge(vec)){ 101 ++ans; 102 // cerr<<vec.x<<" "<<vec.y<<" vec"<<endl; 103 } 104 } 105 printf("%d",ans); 106 return 0; 107 }
2020.7.14 第二十七题:https://ac.nowcoder.com/acm/contest/5667/B
牛客多校第二场b题。。问所有过原点的圆中,给定点在其边上最多是多少。第一反应圆的反演,反演成直线,然后判断一条不过原点直线最多多少点在其上,结果。。反演后精度跪了,也就是判断反演后的点是不是在同一直线上精度误差不行,所以我们不应该判断是不是在同一直线上,而是通过反演图形,判断正形是不是在同一圆上来判断反演图形是不是统一直线上。(好像可以不用圆的反演做,可以根据圆的一些性质,比如中垂线,同一圆弧对角相同。。但是我第一反应就是定点圆。。圆的反演。。思路上秒杀。。)
#include<bits/stdc++.h> using namespace std; #define eps 1e-6 #define ld double const int N = 2e3+9; int n,ans; vector<int> maybe; struct Point{ ld x,y; int id; Point operator - (const Point& b)const{ return (Point){x-b.x,y-b.y}; } Point operator + (const Point& b)const{ return (Point){x+b.x,y+b.y}; } Point operator * (ld b){ return (Point){b*x,y*b}; } ld len(){ return sqrt(x*x + y*y); } ld len2(){ return x*x + y*y; } }pt[N],p[N]; struct Circle{ Point o; ld r; }; ld cross(Point a, Point b){ return a.x*b.y - b.x*a.y; } ld dot(Point a,Point b){ return a.x*b.x + a.y*b.y; } ld dis(Point a,Point b){ return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)); } ld dis2(Point a,Point b){ return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); } int Quadrant(Point a) { if(a.x>=0&&a.y>=0) return 1; if(a.x<=0&&a.y>=0) return 2; if(a.x<=0&&a.y<=0) return 3; return 4; } bool cmp(Point a,Point b) //先按象限从小到大排序 再按极角从小到大排序 { if(Quadrant(a)==Quadrant(b)){ if( fabs( cross(a,b) ) < eps ) return a.len() < b.len(); return cross(a,b) > 0; } else return Quadrant(a)<Quadrant(b); } Point Point_Inver(Circle c0,Point P){ Point OP = P - c0.o; ld len = dis2(c0.o,P); return c0.o + OP*( c0.r * c0.r / len ); } Point core(Point a,Point b,Point c){ Point A=(Point){b.x-a.x,c.x-a.x}*2 , B = (Point){b.y-a.y,c.y-a.y}*2; Point C = (Point){ b.len2()-a.len2(),c.len2()-a.len2() }; return (Point){ cross(B,C)/cross(B,A),cross(A,C)/cross(A,B) }; } bool incir(Point o,double r,Point a){ double d = dis(o,a); if(fabs(d-r) < eps ) return 1; return 0; } void solve(){ if(n==1){ ans = 1; return; } ans = 1; Point p0 = (Point){0,0}; for(int i = 1;i<=n;++i){ vector<Point> tem; for(int j = 1;j<=n;++j){ if(j==i) continue; Point t = p[j] - p[i]; t.id = p[j].id; tem.push_back(t); } sort(tem.begin(),tem.end(),cmp); for(int k = 0; k < tem.size();){ // printf("%.12f\n", fabs(cross(p0,tem[k])) ); if( cross(pt[p[i].id] , pt[tem[k].id]) == 0 ){ ++k; continue; } Point o = core(p0,pt[ p[i].id ] , pt[ tem[k].id ]); double r = o.len(); int j = k; while( j + 1 < tem.size() && incir(o,r,pt[ tem[j+1].id ]) ){ ++j; } ans = max(ans,j - k + 2); k = j + 1; } } // printf("%d",ans); } int main(){ scanf("%d",&n); Circle c0; c0.o = (Point){0,0}; c0.r = 200; for(int i =1;i<=n;++i){ int x,y; scanf("%d %d",&x,&y); // scanf("%Lf %Lf",&pt[i].x,&pt[i].y); pt[i].x = x , pt[i].y = y; p[i] = Point_Inver(c0,pt[i]); p[i].id = i; // cerr<<i<<" "<<p[i].x<<" "<<p[i].y<<endl; // printf("%d %.12lf %.12lf\n",i,p[i].x,p[i].y); } solve(); printf("%d",ans); // for(int i = 1;i<=n;++i) cerr<<p[i].x<<" "<<p[i].y<<" !"<<endl; }
2020.7.14 第二十八题:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=893&pid=1001
给你两类点A,B类点,都是500个,问你用最少能用多少的A点包住B点。
一直以为是什么维护凸包。结果发现,枚举A中两点,假如B的点都在A左侧,那么则连一条有向边,最后就是变成图论,floyd求最小环
#include<bits/stdc++.h> using namespace std; typedef long long ll; #define inf 0x3f3f3f3f const int N = 500+9; struct Point{ int x,y; }pn[N],pm[N]; int cross(Point a,Point b,Point c){ return (b.x-a.x) * (c.y-a.y) - (c.x-a.x)*(b.y-a.y); } int dot(Point a,Point b,Point c){ return (b.x-a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y); } int dis[N][N]; int n,m; bool onSeg(Point a,Point b,Point c){ return dot(c,a,b) <= 0; } bool judge(int x,int y){ for(int i = 1;i<=n;++i){ if( cross(pm[x],pm[y],pn[i]) > 0 || cross(pm[x],pm[y],pn[i]) == 0 && onSeg(pm[x],pm[y],pn[i]) ) continue; return 0; } return 1; } void solve(){ for(int k = 1;k<=m;++k){ for(int i = 1;i<=m;++i){ for(int j = 1;j<=m;++j){ dis[i][j] = min(dis[i][j] , dis[i][k] + dis[k][j]); } } } int mi = inf; for(int i = 1;i<=m;++i) mi = min(mi,dis[i][i]); if( mi == inf ){ puts("ToT"); } else printf("%d\n",m-mi); } int main(){ while(~scanf("%d",&n)){ for(int i = 1;i<=n;++i) scanf("%d %d",&pn[i].x,&pn[i].y); scanf("%d",&m); for(int i = 1;i<=m;++i) scanf("%d %d",&pm[i].x,&pm[i].y); for(int i = 1;i<=m;++i) for(int j = 1;j<=m;++j) dis[i][j] = inf; for(int i = 1;i<=m;++i){ for(int j = 1;j<=m;++j){ if( j == i ) continue; if(judge(i,j)) dis[i][j] = 1; } } solve(); } }