几何+鸽笼原理+dsu复杂度分析——cf995C
这题的证明用了鸽笼原理,感觉真的很精妙
自己傻逼写错了个地方,调了老半天
/* 性质,三个向量里必定存在两个向量,相加后模长<=r 证明:三个向量(以及三个对应的反方向向量)中, 必定有两个向量的夹角在[0,60]之间 -> 必有两个向量夹角在[120,180]之间 所以三个合并成两个,重复这个过程, 用两个set维护当前两个向量,碰到要合并时小的往大的合并 */ #include<bits/stdc++.h> using namespace std; #define ll long long #define N 200005 const double pi = acos(-1.0); struct point{ ll x,y,id,flag; bool operator<(const point & a)const { return id<a.id; } }p[N]; int n,ans[N]; point k1,k2; set<point>s1,s2; point operator + (const point & b,const point &a){ point res; res.x=b.x+a.x;res.y=b.y+a.y; return res; } int check(point k1,point k2,ll D=1000000ll){ point k=k1+k2; ll dis2=k.x*k.x+k.y*k.y; if(dis2<=(ll)D*D && abs(k.x)<=D && abs(k.y)<=D)return 1; return 0; } point inv(point k){ point res=k; res.x*=-1;res.y*=-1;res.flag*=-1; return res; } int main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%lld%lld",&p[i].x,&p[i].y); p[i].flag=1,p[i].id=i; } if(n==1){ cout<<1<<"\n";return 0; } s1.insert(p[1]);s2.insert(p[2]); k1=p[1];k2=p[2]; for(int i=3;i<=n;i++){ if(s1.size()<s2.size())swap(s1,s2),swap(k1,k2); int f=0; if(check(k1,p[i])){ s1.insert(p[i]); k1=k1+p[i];f=1; } else if(check(k1,inv(p[i]))){ s1.insert(inv(p[i])); k1=k1+inv(p[i]);f=2; } else if(check(k2,p[i])){ s2.insert(p[i]); k2=k2+p[i];f=3; } else if(check(k2,inv(p[i]))){ s2.insert(inv(p[i])); k2=k2+inv(p[i]);f=4; } else if(check(k1,k2)){ for(auto p:s2){ s1.insert(p); k1=k1+p; } s2.clear(); s2.insert(p[i]);k2=p[i];f=5; } else if(check(k1,inv(k2))){ for(auto p:s2){ s1.insert(inv(p)); k1=k1+inv(p); } s2.clear(); s2.insert(p[i]);k2=p[i];f=6; } } if(check(k1,k2,1500000ll)){ for(auto p:s2) s1.insert(p); }else if(check(k1,inv(k2),1500000ll)){ for(auto p:s2) s1.insert(inv(p)); } if(s1.size()!=n){puts("error");} for(auto p:s1) ans[p.id]=p.flag; for(int i=1;i<=n;i++)cout<<ans[i]<<" "; }