Kickstart Round D 2018 B题 Paragliding

Kickstart Round D 2018 B题 Paragliding_第1张图片

题目大意:有N个塔,水平坐标为 p[i], 高度为 h[i], 每个塔的水平坐标各不相同。 有K个气球,每个气球可看做一个点,坐标为x[i], y[i]。一个人可以爬到每个塔的任意高度位置,然后在该位置可以向左右45度滑行,滑行的轨迹是直线。如果在途中遇到气球,则可获得该气球,如果气球和塔相重叠,也认为可获得气球,要求一共可以获得多少个气球。

解题关键点:

1. 如果从一个塔( p[j], h[j]  )向下滑行可以获得某个气球(x[i], y[i]), 则满足 abs(x[i] - p[j] ) + y[i] <= h[j]

2. 如果一个塔 i 和另外一个塔 j 满足 abs(p[i] - p[j]) + h[i] <= h[j], 则第i个塔是多余的,因为任意从第i个塔滑行获得的气球,都可以通过从第j个塔滑行获得。

解题方法:

1. 小数据: 将每个气球和所有的塔进行关键点1条件比较,如果满足,则该气球可获得。

2. 大数据: 对于每个气球,如果我们能找到能它最近的塔,只判断是否能从离它最近的塔上获得就好了。但是存在这样一个问题,如果从离它最近的塔上不能获得,也许存在离它较远位置的塔,从这些塔上获得气球。这样的话,还得依次遍历所有的塔。为了达到最初的想法,考虑关键点2, 如果我们把所有多余的塔去掉,那么就可以只判断是否能从离它最近的塔上获得就可以了。为什么? 假设某个气球坐标为(x, y) , 离它最近的右边的塔为(p1, h1),从该塔不能获得气球,则满足 p1 - x + y > h1, 该式子等于 -p1 + x - y + h1 < 0, 又假设存在离该气球更远的塔(p2, h2 )  p2 >p1, 从该塔上可以获得气球,则满足 p2 - x + y <= h2, 将这个式子和前面的式子相加得到  p2 - p1 + h1 <= h2,正好关键点2中的条件,也就是说离它最近的那个塔是多余的。 所以,我们把所有多余的塔去掉之后,就可以找到离某个气球最近的塔,找的过程可以将所有的塔从小到大排好序,再使用二分搜索进行查找。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;

const int maxn = 100010;
ll p[maxn], h[maxn], x[maxn], y[maxn];
ll relevantT[maxn];
ll towerSorted[maxn];//排好序的塔
map valueAndID; //塔的位置和索引
ll A[5], B[5], C[5], M[5];
int T, N, K;

int getRelation(int i, int j)//第i个塔, 第j个塔 i > j
{
    if(h[j] >= p[i] - p[j] + h[i]) //第i个塔是多余的
        return 1;
    if(h[i] >= p[i] - p[j] + h[j]) //第j个塔是多余的
        return 2;
    return 3;
}

bool ok(int i, int j)//第i个气球,第j个塔
{
    if(abs(x[i] - p[j]) + y[i] <= h[j])
        return true;
    return false;
}


int getRelevantTowers()
{
    for(int i = 1; i <= N; i++)
    {
        towerSorted[i] = p[i];
        valueAndID[p[i]] = i;
    }
    sort(towerSorted + 1, towerSorted + 1 + N);

    stack st;
    for(int i = 1; i <= N; i++)
    {
        if(st.empty())
        {
            st.push(towerSorted[i]);
            continue;
        }

        int relation = getRelation(valueAndID[towerSorted[i]], valueAndID[st.top()]);

        if(relation == 1)//多余的
            continue;
        if(relation == 2)//栈顶的塔是多余的
        {
            st.pop();
            while(!st.empty() && getRelation(valueAndID[towerSorted[i]], valueAndID[st.top()]) == 2)
            {
                st.pop();
            }
        }
        st.push(towerSorted[i]);
    }
	
    int len = st.size();
    int tp = len;
    while(!st.empty())
    {
        relevantT[tp--] = st.top();
        st.pop();
    }
	
    return len;
}

int main()
{
    freopen("B-large-practice.in", "r", stdin);
    freopen("out2.txt", "w", stdout);
    cin >> T;
    for(int cas = 1; cas <= T; cas++)
    {
        valueAndID.clear();
        cin >> N >> K;

        cin >> p[1] >> p[2] >> A[1] >> B[1] >> C[1] >> M[1];
        for(int i = 3; i <= N; i++)
            p[i] = (A[1] * p[i - 1] + B[1] * p[i - 2] + C[1]) % M[1] + 1;

        cin >> h[1] >> h[2] >> A[2] >> B[2] >> C[2] >> M[2];
        for(int i = 3; i <= N; i++)
            h[i] = (A[2] * h[i - 1] + B[2] * h[i - 2] + C[2]) % M[2] + 1;

        cin >> x[1] >> x[2] >> A[3] >> B[3] >> C[3] >> M[3];
        for(int i = 3; i <= K; i++)
            x[i] = (A[3] * x[i - 1] + B[3] * x[i - 2] + C[3]) % M[3] + 1;

        cin >> y[1] >> y[2] >> A[4] >> B[4] >> C[4] >> M[4];
        for(int i = 3; i <= K; i++)
            y[i] = (A[4] * y[i - 1] + B[4] * y[i - 2] + C[4]) % M[4] + 1;

        int len = getRelevantTowers();
        int ans = 0;

        for(int i = 1; i <= K; i++)
        {
            int L = lower_bound(relevantT + 1, relevantT + 1 + len, x[i]) - relevantT;

            //和塔重叠
            if(x[i] == relevantT[L])
            {
                if(ok(i, valueAndID[relevantT[L]]))
                    ans++;
                continue;
            }

            //该气球在最左边
            if(L == 1)
            {
                if(ok(i, valueAndID[relevantT[L]]))
                    ans++;
                continue;
            }
			//该气球在最右边
            if(L > len)
            {
                if(ok(i, valueAndID[relevantT[len]]))
                    ans++;
                continue;
            }

            //该气球处在两个塔之间
            int LL = L - 1;
            int RR = L;
            if(ok(i, valueAndID[relevantT[LL]]))
            {
                ans++;
                continue;
            }
            if(ok(i, valueAndID[relevantT[RR]]))
                ans++;
        }
        cout << "Case #" << cas <<": " << ans <

 

你可能感兴趣的:(ACM题目,Kickstart,Round,D)