BZOJ(本校) 2666 中继系统 - 思维&最小生成树

Time Limit: 2s
Memory Limit: 256MB
【题目描述】
这是2112年,人类已经征服了太阳系。太空游侠队已经在任何大块岩石上建立了基地(即使不适宜居住)。你作为小行星通讯部门的一员,工作是确保所有太空游侠小行星基地都能尽可能廉价地与其他小行星基地交流。你可以建立从每个基地到另外所有基地的直接交流连接,但那可能过分昂贵。相反,你想要建立最少数量的连接从而每个人都可以发送信息给其他所有人,信息可能通过一个或多个基地中转。建立任何连接的费用与它连接的两个基地之间的(欧几里德)距离成比例,所以这个问题看起来不怎么难。
但这只是一个小小的困难。小行星有一个运动的趋势,所以两个当前很接近的基地在将来不一定还是很接近。因此随着时间流逝,你一定会乐意转换你的交流连接,从而在任何时候你都拥有最廉价的中继系统。转换这些连接花费时间和金钱,所以你对于了解将要执行多少次转换很感兴趣。
一些假设让这个任务更简单。每个小行星可以视为一个点。小行星总是以固定的速度沿直线运动。没有小行星会和其他小行星相撞。此外,任何在时刻t(t≥0)变得最优的中继系统在任何时刻s(s满足t< s< t+10^-6)时是独一无二最优的。初始最优的中继系统也是独一无二的。

【输入格式】
第一行输入一个N,表示小行星基地的数量。
接下来N行,每行包含6个整数x, y, z, vx, vy, vz,前三个表示这个小行星的初始位置,后三个表示小行星在x, y, z三维上的速度(单位空间每单位时间)。

【输出格式】
对于每组数据,输出一行,包含数据编号和中继系统需要被建立或修改的次数。

【样例输入1】
3
0 0 0 0 0 0
5 0 0 0 0 0
10 1 0 -1 0 0
【样例输出1】
3

【样例输入2】
4
0 0 0 1 0 0
0 1 0 0 -1 0
1 1 1 3 1 1
-1 -1 2 1 -1 -1
【样例输出2】
3

【数据规模与约定】
5%的数据满足n=2。
15%的数据满足n<10。
另10%的数据满足 所有点运动方向和速度一致。
另5%的数据满足 只有一个点与其他点的运动方向和速度不同。
100%的数据满足n≤50,-150≤x,y,z≤150,-100≤vx,vy,vz≤100。

分析:

  • 题目大意就是让你求若干时刻的最小生成树,如果生成树的边发生变化,那么ans++
    但很明显这个”若干时刻“我们没法处理。
    考虑Kruscal的做法:将所有边排序,按顺序,取不在同一联通块里的边。
    那么注意到:对于所有的边,我们不用关注它的具体大小(长度每时每刻都在变,也没法关注),只需要关注它们的相对大小即可。

  • 那么对于n*n条边,边两两组合,对于每一组边的组合,求出它们相等的时刻(实质是解一元二次方程),共有n4个时间点。
    对时间点排序,先求出最开始的最小生成树。
    对于某时间点发生关系改变的两条边,如果都不在树上或者都在树上,完全没意义;将不再树上的边加入树中,再求一次最小生成树,如果生成树的边组成变化,就ans++

  • 大概就是这样了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN 50
const double eps=10e-7;

struct Line{
    int u,v;
    double w;
}L[MAXN*MAXN+10],reb[2][MAXN+10],now[MAXN*MAXN+10];
struct node{
    int u,v;
    double t;
}event[MAXN*MAXN*MAXN*MAXN+10];
int n,p[MAXN+10][20];
int cnte,cntl,cntnow,cntr[2];
int uni[MAXN+10];
bool mat[MAXN+10][MAXN+10];

void read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int k=1;k<=6;k++)
            scanf("%d",&p[i][k]);
}
double Pf(double x){
    return x*x;
}
void Equation(double &a,double &b,double &c,int i,int j){
    a=b=c=0.0;
    for(int k=1;k<=3;k++){
        a+=Pf(p[i][k+3]-p[j][k+3]);
        b+=(p[i][k]-p[j][k])*(p[i][k+3]-p[j][k+3]);
        c+=Pf(p[i][k]-p[j][k]);
    }
    b*=2;
}
void Insert_time(int i,int j,double t){
    if(t<=0) return ;
    ++cnte;
    event[cnte].u=i,event[cnte].v=j;
    event[cnte].t=t;
}
bool cmp1(node a,node b){
    return a.t<b.t;
}
void prepare()
{
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            ++cntl;
            L[cntl].u=i,L[cntl].v=j;
            L[cntl].w=0.0;
            for(int k=1;k<=3;k++)
                L[cntl].w+=Pf(1.0*p[i][k]-p[j][k]);
        }
    double a1,b1,c1,a2,b2,c2,a,b,c,delta;
    for(int i=1;i<=cntl;i++){
        Equation(a1,b1,c1,L[i].u,L[i].v);
        for(int j=i+1;j<=cntl;j++){
            Equation(a2,b2,c2,L[j].u,L[j].v);
            a=a1-a2,b=b1-b2,c=c1-c2;
            if(fabs(a)<eps){
                if(fabs(b)<eps)
                    continue;
                Insert_time(i,j,-c/b);
            }
            else{
                delta=b*b-4.0*a*c;
                if(delta<0) continue;
                Insert_time(i,j,(-b+sqrt(delta))/(2*a));
                Insert_time(i,j,(-b-sqrt(delta))/(2*a));
            }
        }
    }
    sort(event+1,event+cnte+1,cmp1);
}
bool cmp2(Line a,Line b){
    return a.w<b.w;
}
int find(int u){
    if(u==uni[u]) return u;
    return find(uni[u]);
}
void Kruscal()
{
    memset(mat,0,sizeof mat);
    sort(L+1,L+cntl+1,cmp2);
    for(int i=1;i<=n;i++)
        uni[i]=i;
    for(int i=1,r1,r2;i<=cntl;i++){
        r1=find(L[i].u),r2=find(L[i].v);
        if(r1!=r2){
            uni[r2]=r1;
            now[++cntnow]=L[i];
            mat[L[i].u][L[i].v]=mat[L[i].v][L[i].u]=true;
        }
    }
}
int Isin(Line a){
    return mat[a.u][a.v]?1:0;
}
void Rebuild(int id,double t)
{
    for(int i=1,u,v;i<=cntnow;i++){
        u=now[i].u,v=now[i].v;
        now[i].w=0;
        for(int k=1;k<=3;k++)
            now[i].w+=Pf(1.0*p[u][k]+t*p[u][k+3]-p[v][k]-t*p[v][k+3]);
    }
    for(int i=1;i<=n;i++)
        uni[i]=i;
    sort(now+1,now+cntnow+1,cmp2);
    cntr[id]=0;
    for(int i=1,u,v,r1,r2;i<=cntnow;i++){
        u=now[i].u,v=now[i].v;
        r1=find(u),r2=find(v);
        if(r1!=r2){
            uni[r2]=r1;
            ++cntr[id];
            reb[id][cntr[id]].u=u,reb[id][cntr[id]].v=v;
        }
    }
}
bool cmp3(Line a,Line b){
    if(a.u==b.u) return a.v<b.v;
    return a.u<b.u;
}
void workout()
{
    int ans=1;
    Kruscal();
    sort(L+1,L+cntl+1,cmp3); //Kruscal之后要排回来
    for(int i=1,flag=0,p1,p2;i<=cnte;i++){ //flag:同一时间,有没有加边
        p1=Isin(L[event[i].u]),p2=Isin(L[event[i].v]);
        if((p1&&!p2)||(!p1&&p2)){
            if(!p1) now[++cntnow]=L[event[i].u];
            else now[++cntnow]=L[event[i].v];
            flag=1;
        }
        if(fabs(event[i].t-event[i+1].t)>eps&&flag){ //同一时间可能有多条边的相对大小关系发生变化
            Rebuild(0,event[i].t-eps);
            Rebuild(1,event[i].t+eps);
            sort(reb[0]+1,reb[0]+cntr[0]+1,cmp3);
            sort(reb[1]+1,reb[1]+cntr[1]+1,cmp3);
            int g=0;
            cntnow=0;
            memset(mat,0,sizeof mat);
            for(int i=1;i<n;i++){
                if(!(reb[0][i].u==reb[1][i].u&&reb[0][i].v==reb[1][i].v))
                    g=1;
                now[++cntnow]=reb[1][i];
                mat[reb[1][i].u][reb[1][i].v]=mat[reb[1][i].v][reb[1][i].u]=true;
            }
            ans+=g;
        }
        if(fabs(event[i].t-event[i+1].t)>eps)
            flag=0;
    }
    printf("%d\n",ans);
}
int main()
{
    read();
    prepare();
    workout();
    return 0;
}
posted @ 2016-03-07 17:10  KatarinaYuan  阅读(202)  评论(0编辑  收藏  举报