[CODEVS 1244] 云中通信

时间:2021-11-18 00:32:12

描述

天空中有n朵云,在风吹之下以恒定速度v=(vx,vy) 向同一个方向持续移动,也就是说,当时间为t(t≥0)时,云上初始坐标为(x, y)的点移到坐标为( x + t*vx, y + t*vy)的位置。

为简单起见,我们假设云是多边形的(而且其顶点具有整数坐标)。多边形不一定是凸的,但是每个多边形的任意两条边不相交(允许具有公共的端点)。云和云可能会重叠。

地面上有一人造卫星控制中心,位于坐标(0,0)处,在控制中心正上方的云层之上,有一颗人造卫星。一道激光束从控制中心笔直地向上射向人造卫星,这道激光束用于与卫星进行通信。然而,当激光束的光路被云遮住时,通信就会中断。最初激光束不与任何一片云相交。在观测期间,会有若干个这样的时段,在这些时段期间激光束的光路穿过一片或几片云,使得通信中断。甚至当激光束遇到云的某个顶点时,通信也会有瞬间的中断。你需要写一个程序来计算在所有云移过之前,通信会中断多少次。

http://codevs.cn/problem/1244/


分析

这个题题意一直理解的不对, 是只要激光束被挡住就算通信终端还是发射激光的点被挡住才算通信终端? 看样例是后者, 但光沿直线传播的道理是不能被更改的… …

好吧, 实在不会写, 把标程注释翻译了一遍 (原文是英文注释), 加了些自己的理解, 也不知道对不对.

感觉太高端了, 手写分数类、手写62位大整数乘法… 晕!


代码

1367ms 19MB


跟原程序比把随机调换交点顺序的代码删除了, 搞不懂怎么回事, 而且那段代码风格都和其他地方不一样. 闹哪样?

/*************************************************************************} {* *} {* CEOI 2004 *} {* *} {* Sample solution: Clouds *} {* File: CLO.CPP *} {* Author: PIOTR STANCZYK *} *************************************************************************/
#include <cstdio>
#include <algorithm>
#include <vector>
#define MAX_CLOUDS 1000
#define CLOUD_SIZE 1000
#define LOCAL

typedef long long int lli;
typedef unsigned long long int ulli;
const ulli low_part  = 2147483647;                /* 2^31 - 1 */ // 31 个 1 
const ulli high_part = low_part * (low_part + 2); /* 2^62 - 1 */ // 62 个 1 
using namespace std;

struct point {
    int x,y;
};

struct superlong {
    ulli high, low;
};

struct crossing {  /* Intersection point */ // 云和激光的交点 
    lli posh, posl;  /* Relative position of the point */ // 在激光上的位置 (比例) 
    short int cloud; /* Owner of the point */ // 所在的云 
    char type;       /* Type of intersection */ // 交点类型 
};

int vel_x, vel_y, clouds; /* Velocity vector and the number of clouds */ // 移动速度正交分解 
vector<crossing> cr;      /* Vector of intersection points */ // 存激光和云的交点 


inline superlong multiply(ulli v1, ulli v2)
/* Multiplies two unsigned long long ints and returns the result as superlong. * * This function assumes that multiplied values are at most 62-bits long */ // 两个无符号长整型 v1,v2 相乘的结果 
{
    superlong val;
    ulli x = (v1 & low_part) * (v2 >> 31) + (v1 >> 31) * (v2 & low_part); 
    // x = v1 的后31位乘上 v2 右移31的结果 + v1 的后31位乘上 v2 右移31的结果 = ??
    val.low = (v1 & low_part) * (v2 & low_part);
    val.high = (v1 >> 31) * (v2 >> 31);
    val.low  += (x & low_part) << 31;
    val.high += (x >> 31) + (val.low >> 62);
    val.low  = val.low & high_part;
    return val;
}

inline int compare(const crossing& a, const crossing& b)
/* Compares a position of two given crossing points */
// 先考虑高位再考虑低位判断大小 
{
    // a : a.posh / a.posl ( > 0 )
    // b : b.posh / b.posl ( > 0 )
    // a - b : a.posh*b.posl - a.posl*b.posh / (a.posl*b.posl)
    // 在正负符号上 = a.posh*b.posl - a.posl*b.posh
    superlong a1 = multiply(a.posh, b.posl);
    superlong b1 = multiply(a.posl, b.posh);
    if (a1.high == b1.high) {
        if (a1.low == b1.low) return 0; // a = b
        if (a1.low < b1.low) return -1; // a < b
        return 1; // a > b
    }
    if (a1.high < b1.high) return -1;
    return 1;  
}

inline bool cmp(const crossing& a, const crossing& b)
{
    return (compare(a, b) == -1); // return a < b (比较a, b到激光发射点 (point [0,0]) 的相对距离大小) 
}

int side(const point& a)
/* Determines the location of a given point against velocity vector */ // 计算给定顶点和速度向量的相对位置 
{
    lli x = (lli)vel_x * (lli)a.y - (lli)vel_y * (lli)a.x; // 向量 (vel_x, vel_y) 与 向量 (a.x, a.y) 的 叉积
    if (x == 0) return 0; // 两个向量重合 即速度向量过该点 
    if (x > 0) return 1;  // 点在速度向量上方 
    return -1;            // 点在速度向量下方 
}

void Add_intersection(const point& a, const point& b, short int cloud, char type)
/* Examines an intersection point between velocity vector and (a,b) segment */
// 检查 速度向量 和 线段ab 的交点情况 
{
    crossing c;
    /* The relative distance of the crossing point from the laser beam (point [0,0]) * * is defined as c.posh/c.posl. In order to keep arithmetic precision we have * * to represent this value as a fraction */
    // 交点和激光发射点 (point [0,0]) 的相对距离用 c.posh/c.posl 表示, 采用手写分数类的方式, 来避免精度误差 
    c.posh = lli (b.y) * lli (a.x) - lli (b.x) * lli (a.y);
    c.posl = lli (vel_y) * lli (a.x - b.x) - lli (vel_x) * lli (a.y - b.y);
    if (c.posh < 0 && c.posl < 0) { // 分子分母负负得正 
        c.posh = -c.posh;
        c.posl = -c.posl;
    } 
    /* Examined intersection is not important for as */ 
    // 距离为负, 交点在激光的反向延长线上, 舍掉答案
    else if (c.posh < 0 || c.posl < 0) return; 

    c.cloud = cloud;
    c.type = type;
    cr.push_back(c);
}

void Read_Data()
/* Reads data and finds all intersection points */ // 读入数据, 找到所有速度向量与云的交点 
{
    int size;
    point cloud[CLOUD_SIZE]; // 保存云上的顶点 
    scanf("%d %d %d", &clouds, &vel_x, &vel_y);

    // 运用爱因斯坦狭义相对论的原理对速度进行等效的处理, 相当于云不动, 坐标中心反方向运动 
    vel_x = -vel_x;
    vel_y = -vel_y;

    for(int x = 0; x < clouds; ++x) { // 一共 clouds 朵云 
        scanf("%d", &size);
        for(int y = 0; y < size; ++y) {
            scanf("%d %d", &cloud[y].x, &cloud[y].y);
        }
        int pos = 0, f_side, l_side;

        /* Finds a vertex not located above velocity vector */
        // 找到一个不位于速度向量上的顶点 速度向量不过该点 
        while ( (f_side = side(cloud[pos])) == 0) ++pos; 

        int y = 1;
        while (y <= size) {
            l_side = f_side; // l_side 一定不能等于 0 
            f_side = side(cloud[(pos + y) % size]); // 与当前顶点相邻的下一个顶点与速度向量的相对位置 
            switch (l_side * f_side) {

                case 1 :
                /* Vertices are located on the same side -> no intersection */ 
                // 两顶点位于速度向量的同一侧, 连线和速度向量无交点 
                ++y;
                break;

                case -1 : 
                /* Vertices are located on a different sides -> intersection found */ 
                // 两顶点位于速度向量的不同侧, 连线和速度向量有交点 
                Add_intersection(cloud[(pos + y - 1) % size], cloud[(pos + y) % size], x, 0);
                ++y;
                break;

                case 0 : 
                /* Vertex is located directly above velocity vector -> further verification */
                // (pos + y) 位于速度向量上 
                int beg = pos + y;
                while ( (f_side = side(cloud[(pos + y) % size])) == 0) ++y;
                if (pos + y != beg + 1) {
                    // 有很多交点, 但只记录下两个 
                    Add_intersection(cloud[(pos + y) % size], cloud[(pos + y - 1) % size], x, (l_side == f_side) ? 1 : 2);
                    Add_intersection(cloud[(beg - 1) % size], cloud[beg % size], x, 1);
                } else { 
                    // 只有一个交点 (pos + y), 如果没有穿过激光, type = 3, 否则 type = 0 
                    Add_intersection(cloud[(pos + y - 1) % size], cloud[(pos + y) % size], x, (l_side == f_side) ? 3 : 0);
                }
                break;
            }
        }
    }
}

int Count_Result()
/* Searches the sorted list of intersection points and calculates the result */
// 扫描排好序的交点列表, 统计答案 
{
    bool inside[MAX_CLOUDS];  /* is a cloud directly above the laser beam */            // 激光当前是否在该云里面 
    bool on_edge[MAX_CLOUDS]; /* is an edge of a cloud directly above the laser beam */ // 激光当前是否在该云边缘 
    int am_edges = 0, am_inside = 0, result = 0;
    crossing location;
    location.posh = 0; /* Sets the actual location to the */ // 从初始化的距离为 0 开始扫描 
    location.posl = 1; /* initial position of the laser beam */ // 分母不是 0 即可 

    for(int x = 0; x < clouds; ++x)
    inside[x] = on_edge[x] = false;

    for(__typeof (cr.begin()) it = cr.begin(); it != cr.end(); ++it) {
        // 以下三项判断分别对应着: 当前的激光不在云中, 当前的激光不在云的边缘, 当前点与上个点不重合 
        if (am_inside == 0 && am_edges == 0 && compare(location,*it) != 0) 
        ++result;

        location = *it;

        if (location.type == 1 || location.type == 2) 
        /* Intersection changes the state of an edge */ 
        // 交点情况改变边的状态 激光从该云的边缘上移开 或 激光移动到了该云的边缘上 
        (on_edge[location.cloud] = !on_edge[location.cloud]) ? ++am_edges : --am_edges;

        if (location.type == 0 || location.type == 2) 
        /* Intersection changes the state of a cloud */ 
        // 交点情况改变云的状态 激光从该云中移出 或 激光进入该云 
        (inside[location.cloud] = !inside[location.cloud]) ? ++am_inside : --am_inside;
    }
    return result;
}

int main()
{
    Read_Data();

    // 把交点按照距离激光发射点 (point [0,0]) 的相对距离从近到远排序 
    sort(cr.begin(), cr.end(), cmp);

    printf("%d\n", Count_Result());
    return 0;
}