2012-A3-0014
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
题意:有N组,每组有2个球心坐标,要从每组中选一个球心,使得选取的球之间两两不相交的半径最大。
i和i+n是二选一的一组,先计算所有不是同一组的球两两之间的距离,然后从小到大排序。
在上面计算的距离中二分距离,然后距离小于等于二分值的每个点对(a,b)(A和a同组,B和b同组)
建立有向边a->B, b->A,因为a,b不能同时在,那么选了a就要选B,选了b就要选A...
最后如果边确定了,某个点i+n->i ,且i->i+n,那么这种方案是不可行的,所以答案值比它小,
即 i+n 和 i 在同一个强连通分量里面.
注意题目中的舍入方式是向下取整.
{{{
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef size_t u32;
typedef pair<u32,u32> dij;
dij ee[80000];
int x[400],y[400],z[400];
u32 n;
u32 sqr(int x) {return x*x;}
u32 dist(u32 i,u32 j){return sqr(x[i]-x[j])+sqr(y[i]-y[j])+sqr(z[i]-z[j]);}
u32 neg(u32 x) {return (x<n)?x+n:x-n;}
namespace Gabow{
const u32 MAXN = 400;
u32 e[80000],nx[80000],p[MAXN],pre[MAXN],
id[MAXN],s[MAXN+1],r[MAXN+1],m,n,S,R,c,t;
#define clr(x) memset(x,0xff,n<<2)
void init(u32 _){n=_;m=S=R=c=t=0;clr(p);clr(id);clr(pre);}
void ae(u32 a,u32 b){e[m]=b;nx[m]=p[a];p[a]=m++;}
void dfs(u32 u){
pre[s[++S]=r[++R]=u]=t++;
for(u32 i=p[u],v;~i;i=nx[i])
if(~pre[v=e[i]]){
if(!~id[v])while(pre[r[R]]>pre[v])--R;
}else dfs(v);
if(r[R]==u){--R;do id[s[S]]=c;while(s[S--]!=u);c++;}
}
u32 scc(){for(u32 i=0;i<n;++i)if(!~pre[i])dfs(i);return c;}
}
int main(){
for(u32 m,ans;~scanf("%u",&n);){
for(u32 i=m=0;i<n;++i){
scanf("%d%d%d%d%d%d",x+i,y+i,z+i,x+i+n,y+i+n,z+i+n);
for(u32 j=0;j<i;++j){
ee[m++]=dij(dist(i,j),i<<16|j);
ee[m++]=dij(dist(i+n,j),i+n<<16|j);
ee[m++]=dij(dist(i,j+n),i<<16|j+n);
ee[m++]=dij(dist(i+n,j+n),i+n<<16|j+n);
}
}
sort(ee,ee+m);
u32 l=1,r=m-1;
for(u32 x;l+1<r;){
Gabow::init(n<<1);
for(x= l+r>>1;x<r&&ee[x+1].first==ee[x].first;++x);
for(u32 i=0,a,b;i<=x;++i){
Gabow::ae(a=ee[i].second>>16,neg(b=ee[i].second&511));
Gabow::ae(b,neg(a));
}
Gabow::scc();
for(u32 i=0;i<=n;++i)
if(i<n){if(Gabow::id[i]==Gabow::id[i+n]){for(r=x;r>l&&ee[r].first==ee[r-1].first;--r);break;}}
else l=x;
}
l =sqrt(ee[r].first*250000.0);
printf("%u.%u\n",l/1000,l%1000);
}
return 0;
}
}}}
{{{
因为并查集是不对的,边是有向边,反过来不一定成立
我一开始写也想偷懒写成并查集后来发现有问题
By 猛犸也钻地
}}}
题意:有N组,每组有2个球心坐标,要从每组中选一个球心,使得选取的球之间两两不相交的半径最大。
一下做法只能过样例,不能AC,不知是算法错误还是实现错误,求编辑......
我的做法是:i和i+n是二选一的一组,先计算所有不是同一组的球两两之间的距离,然后从小到大排序,
然后从小到大枚举上面求出距离(设这是球A(与他同一组的是A')和球B的距离),如果认为半径的两倍小于此距离,那么这两个球A,B必然不能同时选择,
于是A只能和B'同时选择,B只能和A'可以同时选择,用并查集维护这种关系。
如果枚举到某条距离后,存在某个球心C和他同组的C'在同一个集合时,说明此半径的2倍大于此距离会使选取关系无法满足,此距离除2就是答案?
{{{
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef size_t u32;
typedef pair<u32,u32> dij;
dij ee[80000];
int x[400],y[400],z[400];
u32 f[400],n;
inline u32 sqr(int x) {return x*x;}
inline u32 dist(u32 i,u32 j){return sqr(x[i]-x[j])+sqr(y[i]-y[j])+sqr(z[i]-z[j]);}
inline u32 neg(u32 x) {return (x<n)?x+n:x-n;}
u32 ff(u32 x) {return x!=f[x]?f[x]=ff(f[x]):x;}
void merge(u32 i,u32 j) {f[ff(j)]=ff(i);}
int main(){
for(u32 m,ans;~scanf("%u",&n);){
for(u32 i=m=0;i<n;++i){
scanf("%d%d%d%d%d%d",x+i,y+i,z+i,x+i+n,y+i+n,z+i+n);
for(u32 j=0;j<i;++j){
ee[m++]=dij(dist(i,j),i<<16|j);
ee[m++]=dij(dist(i+n,j),i+n<<16|j);
ee[m++]=dij(dist(i,j+n),i<<16|j+n);
ee[m++]=dij(dist(i+n,j+n),i+n<<16|j+n);
}
}
sort(ee,ee+m);ee[m].first=~0;
for(u32 i=0;i<n<<1;++i)f[i]=i;
for(u32 i=0;i<m;){
ans=ee[i].first;
for(u32 a,b;ee[i].first==ans;++i){
a=ee[i].second>>16;b=ee[i].second&511;
merge(neg(a),b);
merge(neg(b),a);
}
for(u32 j=0;j<n;++j)
if(ff(j)==ff(j+n))
i=m;
}
printf("%.3lf\n",sqrt(ans*0.25));
}
return 0;
}
}}}
题意:有N组,每组有2个球心坐标,要从每组中选一个球心,使得选取的球之间两两不相交的半径最大。
i和i+n是二选一的一组,先计算所有不是同一组的球两两之间的距离,然后从小到大排序。
在上面计算的距离中二分距离,然后距离小于等于二分值的每个点对(a,b)(A和a同组,B和b同组)
建立有向边a->B, b->A,因为a,b不能同时在,那么选了a就要选B,选了b就要选A...
最后如果边确定了,某个点i+n->i ,且i->i+n,那么这种方案是不可行的,所以答案值比它小,
即 i+n 和 i 在同一个强连通分量里面.
注意题目中的舍入方式是向下取整.
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
typedef size_t u32;
typedef pair<u32,u32> dij;
dij ee[80000];
int x[400],y[400],z[400];
u32 n;
u32 sqr(int x) {return x*x;}
u32 dist(u32 i,u32 j){return sqr(x[i]-x[j])+sqr(y[i]-y[j])+sqr(z[i]-z[j]);}
u32 neg(u32 x) {return (x<n)?x+n:x-n;}
namespace Gabow{
const u32 MAXN = 400;
u32 e[80000],nx[80000],p[MAXN],pre[MAXN],
id[MAXN],s[MAXN+1],r[MAXN+1],m,n,S,R,c,t;
#define clr(x) memset(x,0xff,n<<2)
void init(u32 _){n=_;m=S=R=c=t=0;clr(p);clr(id);clr(pre);}
void ae(u32 a,u32 b){e[m]=b;nx[m]=p[a];p[a]=m++;}
void dfs(u32 u){
pre[s[++S]=r[++R]=u]=t++;
for(u32 i=p[u],v;~i;i=nx[i])
if(~pre[v=e[i]]){
if(!~id[v])while(pre[r[R]]>pre[v])--R;
}else dfs(v);
if(r[R]==u){--R;do id[s[S]]=c;while(s[S--]!=u);c++;}
}
u32 scc(){for(u32 i=0;i<n;++i)if(!~pre[i])dfs(i);return c;}
}
int main(){
for(u32 m,ans;~scanf("%u",&n);){
for(u32 i=m=0;i<n;++i){
scanf("%d%d%d%d%d%d",x+i,y+i,z+i,x+i+n,y+i+n,z+i+n);
for(u32 j=0;j<i;++j){
ee[m++]=dij(dist(i,j),i<<16|j);
ee[m++]=dij(dist(i+n,j),i+n<<16|j);
ee[m++]=dij(dist(i,j+n),i<<16|j+n);
ee[m++]=dij(dist(i+n,j+n),i+n<<16|j+n);
}
}
sort(ee,ee+m);
u32 l=1,r=m-1;
for(u32 x;l+1<r;){
Gabow::init(n<<1);
for(x= l+r>>1;x<r&&ee[x+1].first==ee[x].first;++x);
for(u32 i=0,a,b;i<=x;++i){
Gabow::ae(a=ee[i].second>>16,neg(b=ee[i].second&511));
Gabow::ae(b,neg(a));
}
Gabow::scc();
for(u32 i=0;i<=n;++i)
if(i<n){if(Gabow::id[i]==Gabow::id[i+n]){for(r=x;r>l&&ee[r].first==ee[r-1].first;--r);break;}}
else l=x;
}
l =sqrt(ee[r].first*250000.0);
printf("%u.%u\n",l/1000,l%1000);
}
return 0;
}
因为并查集是不对的,边是有向边,反过来不一定成立
我一开始写也想偷懒写成并查集后来发现有问题
By 猛犸也钻地
题意:有N组,每组有2个球心坐标,要从每组中选一个球心,使得选取的球之间两两不相交的半径最大。
一下做法只能过样例,不能AC,不知是算法错误还是实现错误,求编辑......
我的做法是:i和i+n是二选一的一组,先计算所有不是同一组的球两两之间的距离,然后从小到大排序,
然后从小到大枚举上面求出距离(设这是球A(与他同一组的是A')和球B的距离),如果认为半径的两倍小于此距离,那么这两个球A,B必然不能同时选择,
于是A只能和B'同时选择,B只能和A'可以同时选择,用并查集维护这种关系。
如果枚举到某条距离后,存在某个球心C和他同组的C'在同一个集合时,说明此半径的2倍大于此距离会使选取关系无法满足,此距离除2就是答案?
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef size_t u32;
typedef pair<u32,u32> dij;
dij ee[80000];
int x[400],y[400],z[400];
u32 f[400],n;
inline u32 sqr(int x) {return x*x;}
inline u32 dist(u32 i,u32 j){return sqr(x[i]-x[j])+sqr(y[i]-y[j])+sqr(z[i]-z[j]);}
inline u32 neg(u32 x) {return (x<n)?x+n:x-n;}
u32 ff(u32 x) {return x!=f[x]?f[x]=ff(f[x]):x;}
void merge(u32 i,u32 j) {f[ff(j)]=ff(i);}
int main(){
for(u32 m,ans;~scanf("%u",&n);){
for(u32 i=m=0;i<n;++i){
scanf("%d%d%d%d%d%d",x+i,y+i,z+i,x+i+n,y+i+n,z+i+n);
for(u32 j=0;j<i;++j){
ee[m++]=dij(dist(i,j),i<<16|j);
ee[m++]=dij(dist(i+n,j),i+n<<16|j);
ee[m++]=dij(dist(i,j+n),i<<16|j+n);
ee[m++]=dij(dist(i+n,j+n),i+n<<16|j+n);
}
}
sort(ee,ee+m);ee[m].first=~0;
for(u32 i=0;i<n<<1;++i)f[i]=i;
for(u32 i=0;i<m;){
ans=ee[i].first;
for(u32 a,b;ee[i].first==ans;++i){
a=ee[i].second>>16;b=ee[i].second&511;
merge(neg(a),b);
merge(neg(b),a);
}
for(u32 j=0;j<n;++j)
if(ff(j)==ff(j+n))
i=m;
}
printf("%.3lf\n",sqrt(ans*0.25));
}
return 0;
}